@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,60 @@
|
|
|
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 { ConnectorFile, StorageConnector, HostingConnector, StatusCallback, ConnectorSession, contentToString, toConnectorData, ConnectorFileContent} from './connectors'
|
|
19
|
+
import { join } from 'path'
|
|
20
|
+
import { FsStorage } from './FsStorage'
|
|
21
|
+
import { ConnectorType, JobData, JobStatus, PublicationJobData, WebsiteId } from '../../types'
|
|
22
|
+
import { JobManager } from '../jobs'
|
|
23
|
+
|
|
24
|
+
type FsSession = ConnectorSession
|
|
25
|
+
|
|
26
|
+
interface FsOptions {
|
|
27
|
+
path?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class FsHosting extends FsStorage implements HostingConnector<FsSession> {
|
|
31
|
+
connectorId = 'fs-hosting'
|
|
32
|
+
displayName = 'File system hosting'
|
|
33
|
+
connectorType = ConnectorType.HOSTING
|
|
34
|
+
|
|
35
|
+
async publish(session: FsSession, id: WebsiteId, files: ConnectorFile[], {startJob, jobSuccess, jobError}: JobManager): Promise<JobData> {
|
|
36
|
+
const job = startJob(`Publishing to ${this.displayName}`) as PublicationJobData
|
|
37
|
+
job.logs = [[`Publishing to ${this.displayName}`]]
|
|
38
|
+
job.errors = [[]]
|
|
39
|
+
// Call write without id or folder so that it goes in / (path will be modified by publication transformers)
|
|
40
|
+
this.write(session, '', files, '', async ({status, message}) => {
|
|
41
|
+
// Update the job status
|
|
42
|
+
job.status = status
|
|
43
|
+
job.message = message
|
|
44
|
+
job.logs[0].push(message)
|
|
45
|
+
if(status === JobStatus.SUCCESS) {
|
|
46
|
+
jobSuccess(job.jobId, message)
|
|
47
|
+
} else if(status === JobStatus.ERROR) {
|
|
48
|
+
job.errors[0].push(message)
|
|
49
|
+
jobError(job.jobId, message)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
return job
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getUrl(session: FsSession, id: WebsiteId): Promise<string> {
|
|
56
|
+
const filePath = join(this.options.path, id, 'index.html')
|
|
57
|
+
const fileUrl = new URL(filePath, 'file://')
|
|
58
|
+
return fileUrl.toString()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { expect, beforeEach, afterAll, it, describe } from '@jest/globals'
|
|
2
|
+
import { ServerConfig } from '../config'
|
|
3
|
+
import { FsStorage } from './FsStorage'
|
|
4
|
+
import { readFileSync, rmdirSync, statSync } from 'fs'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
|
|
7
|
+
const storageRootPath = '/tmp/silex-tests'
|
|
8
|
+
const assetsFolder = 'assets'
|
|
9
|
+
const dummySession = {}
|
|
10
|
+
const dummyId = 'dummy id'
|
|
11
|
+
const dummyWebsite = {
|
|
12
|
+
pages: [],
|
|
13
|
+
assets: [],
|
|
14
|
+
styles: [],
|
|
15
|
+
settings: {},
|
|
16
|
+
fonts: [],
|
|
17
|
+
symbols: [],
|
|
18
|
+
publication: {},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterAll(() => {
|
|
25
|
+
rmdirSync(storageRootPath, { recursive: true })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
async function sleep(ms: number) {
|
|
29
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
30
|
+
}
|
|
31
|
+
describe('FsStorage website', () => {
|
|
32
|
+
it('should create the root folder', async () => {
|
|
33
|
+
expect(() => statSync(storageRootPath)).toThrow()
|
|
34
|
+
const connector = new FsStorage({} as ServerConfig, {
|
|
35
|
+
path: storageRootPath,
|
|
36
|
+
assetsFolder,
|
|
37
|
+
})
|
|
38
|
+
await sleep(100)
|
|
39
|
+
expect(() => statSync(storageRootPath)).not.toThrow()
|
|
40
|
+
})
|
|
41
|
+
it('should create a new website', async () => {
|
|
42
|
+
const connector = new FsStorage({} as ServerConfig, {
|
|
43
|
+
path: storageRootPath,
|
|
44
|
+
assetsFolder,
|
|
45
|
+
})
|
|
46
|
+
const id = await connector.createWebsite(dummySession, { name: 'dummy name', connectorUserSettings: {} })
|
|
47
|
+
expect(id).toBeDefined()
|
|
48
|
+
expect(() => statSync(join(storageRootPath, id, 'website.json'))).not.toThrow()
|
|
49
|
+
expect(readFileSync(join(storageRootPath, id, 'website.json'), 'utf8')).toBe(JSON.stringify({}))
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Silex website builder, free/libre no-code tool for makers.
|
|
3
|
+
* Copyright (c) 2023 lexoyo and Silex Labs foundation
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs, { stat } from 'fs/promises'
|
|
19
|
+
import { createWriteStream } from 'fs'
|
|
20
|
+
import { ConnectorFile, StorageConnector, StatusCallback, ConnectorSession, toConnectorData, ConnectorFileContent} from './connectors'
|
|
21
|
+
import { dirname, join } from 'path'
|
|
22
|
+
import { ConnectorUser, WebsiteMeta, JobStatus, WebsiteId, ConnectorType, WebsiteMetaFileContent, WebsiteData, defaultWebsiteData, ConnectorOptions } from '../../types'
|
|
23
|
+
import { userInfo } from 'os'
|
|
24
|
+
import { requiredParam } from '../utils/validation'
|
|
25
|
+
import { ServerConfig } from '../config'
|
|
26
|
+
import { DEFAULT_WEBSITE_ID, WEBSITE_DATA_FILE, WEBSITE_META_DATA_FILE } from '../../constants'
|
|
27
|
+
import { Readable } from 'stream'
|
|
28
|
+
import { v4 as uuid } from 'uuid'
|
|
29
|
+
import { fileURLToPath } from 'url'
|
|
30
|
+
|
|
31
|
+
// Variables needed for jest tests
|
|
32
|
+
if(!globalThis.__dirname) {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
globalThis.__dirname = dirname(process.cwd() + '/src/ts/server/connectors/FsStorage.ts')
|
|
35
|
+
console.info('Redefining __dirname', globalThis.__dirname)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type FsSession = ConnectorSession
|
|
39
|
+
|
|
40
|
+
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 = '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'
|
|
42
|
+
|
|
43
|
+
interface FsOptions {
|
|
44
|
+
path: string
|
|
45
|
+
assetsFolder: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class FsStorage implements StorageConnector<FsSession> {
|
|
49
|
+
connectorId = 'fs-storage'
|
|
50
|
+
displayName = 'File system storage'
|
|
51
|
+
icon = FILE_ICON
|
|
52
|
+
disableLogout = true
|
|
53
|
+
options: FsOptions
|
|
54
|
+
connectorType = ConnectorType.STORAGE
|
|
55
|
+
color = '#ffffff'
|
|
56
|
+
background = '#006400'
|
|
57
|
+
|
|
58
|
+
constructor(config: ServerConfig | null, opts: Partial<FsOptions>) {
|
|
59
|
+
this.options = {
|
|
60
|
+
path: join(__dirname, '..', '..', '..', '..', 'data'),
|
|
61
|
+
assetsFolder: '/assets',
|
|
62
|
+
...opts,
|
|
63
|
+
}
|
|
64
|
+
this.initFs()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private async initFs() {
|
|
68
|
+
const stat = await fs.stat(this.options.path).catch(() => null)
|
|
69
|
+
if (!stat) {
|
|
70
|
+
// create data folder with a default website
|
|
71
|
+
const id = DEFAULT_WEBSITE_ID
|
|
72
|
+
await fs.mkdir(join(this.options.path, id, this.options.assetsFolder), { recursive: true })
|
|
73
|
+
await this.setWebsiteMeta({}, id, { name: 'Default website', connectorUserSettings: {} })
|
|
74
|
+
await this.updateWebsite({}, id, defaultWebsiteData)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ********************
|
|
79
|
+
// Job utils methods
|
|
80
|
+
// ********************
|
|
81
|
+
private updateStatus(filesStatuses, status, statusCbk) {
|
|
82
|
+
statusCbk && statusCbk({
|
|
83
|
+
message: `<p>Writing files:<ul><li>${filesStatuses.map(({file, message}) => `${file.path}: ${message}`).join('</li><li>')}</li></ul></p>`,
|
|
84
|
+
status,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private initStatus(files) {
|
|
89
|
+
return files.map(file => ({
|
|
90
|
+
file,
|
|
91
|
+
message: 'Waiting',
|
|
92
|
+
status: JobStatus.IN_PROGRESS,
|
|
93
|
+
}))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ********************
|
|
97
|
+
// Connector interface
|
|
98
|
+
// ********************
|
|
99
|
+
getOptions(formData: object): ConnectorOptions {
|
|
100
|
+
return {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getOAuthUrl(session: FsSession): Promise<null> { return null }
|
|
104
|
+
|
|
105
|
+
async getLoginForm(session: FsSession, redirectTo: string): Promise<string | null> {
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
async getSettingsForm(session: FsSession, redirectTo: string): Promise<string | null> {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async isLoggedIn(session: FsSession): Promise<boolean> {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async setToken(session: FsSession, query: object): Promise<void> {}
|
|
117
|
+
|
|
118
|
+
async logout(session: FsSession): Promise<void> {}
|
|
119
|
+
|
|
120
|
+
async getUser(session: FsSession): Promise<ConnectorUser> {
|
|
121
|
+
const { username, } = userInfo()
|
|
122
|
+
return {
|
|
123
|
+
name: username,
|
|
124
|
+
picture: USER_ICON,
|
|
125
|
+
storage: await toConnectorData(session, this),
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async setWebsiteMeta(session: any, id: string, data: WebsiteMetaFileContent): Promise<void> {
|
|
130
|
+
const websiteId = requiredParam<WebsiteId>(id, 'website id')
|
|
131
|
+
const content = JSON.stringify(data)
|
|
132
|
+
const path = join(this.options.path, id, WEBSITE_META_DATA_FILE)
|
|
133
|
+
await fs.writeFile(path, content)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async getWebsiteMeta(session: FsSession, id: WebsiteId): Promise<WebsiteMeta> {
|
|
137
|
+
const websiteId = requiredParam<WebsiteId>(id, 'website id')
|
|
138
|
+
// Get stats for website folder
|
|
139
|
+
const fileStat = await stat(join(this.options.path, websiteId))
|
|
140
|
+
const path = join(this.options.path, websiteId, WEBSITE_META_DATA_FILE)
|
|
141
|
+
// Get meta file
|
|
142
|
+
const content = await fs.readFile(path)
|
|
143
|
+
const meta = await JSON.parse(content.toString())
|
|
144
|
+
// Return all meta
|
|
145
|
+
return {
|
|
146
|
+
websiteId,
|
|
147
|
+
name: meta.name,
|
|
148
|
+
imageUrl: meta.imageUrl,
|
|
149
|
+
connectorUserSettings: meta.connectorUserSettings,
|
|
150
|
+
createdAt: fileStat.birthtime,
|
|
151
|
+
updatedAt: fileStat.mtime,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ********************
|
|
156
|
+
// Storage interface
|
|
157
|
+
// ********************
|
|
158
|
+
async createWebsite(session: FsSession, meta: WebsiteMetaFileContent): Promise<WebsiteId> {
|
|
159
|
+
const id = uuid()
|
|
160
|
+
await fs.mkdir(join(this.options.path, id, this.options.assetsFolder), { recursive: true })
|
|
161
|
+
await this.setWebsiteMeta(session, id, meta)
|
|
162
|
+
await this.updateWebsite(session, id, defaultWebsiteData)
|
|
163
|
+
return id
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async readWebsite(session: FsSession, websiteId: WebsiteId): Promise<WebsiteData> {
|
|
167
|
+
const id = requiredParam<WebsiteId>(websiteId, 'website id')
|
|
168
|
+
const path = join(this.options.path, id, WEBSITE_DATA_FILE)
|
|
169
|
+
const content = await fs.readFile(path)
|
|
170
|
+
return JSON.parse(content.toString())
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async updateWebsite(session: FsSession, websiteId: WebsiteId, data: WebsiteData): Promise<void> {
|
|
174
|
+
const id = requiredParam<WebsiteId>(websiteId, 'website id')
|
|
175
|
+
const path = join(this.options.path, id, WEBSITE_DATA_FILE)
|
|
176
|
+
await fs.writeFile(path, JSON.stringify(data))
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async deleteWebsite(session: FsSession, websiteId: WebsiteId): Promise<void> {
|
|
180
|
+
const id = requiredParam<WebsiteId>(websiteId, 'website id')
|
|
181
|
+
const path = join(this.options.path, id)
|
|
182
|
+
return fs.rmdir(path, { recursive: true })
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async listWebsites(session: any): Promise<WebsiteMeta[]> {
|
|
186
|
+
const list = await fs.readdir(this.options.path)
|
|
187
|
+
return Promise.all(list.map(async fileName => {
|
|
188
|
+
const websiteId = fileName as WebsiteId
|
|
189
|
+
return this.getWebsiteMeta(session, websiteId)
|
|
190
|
+
}))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async getAsset(session: FsSession, id: WebsiteId, path: string): Promise<ConnectorFile> {
|
|
194
|
+
const fullPath = join(this.options.path, id, this.options.assetsFolder, path)
|
|
195
|
+
const content = await fs.readFile(fullPath)
|
|
196
|
+
return { path, content }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async writeAssets(session: FsSession, id: WebsiteId, files: ConnectorFile[], statusCbk?: StatusCallback): Promise<void> {
|
|
200
|
+
return this.write(session, id, files, this.options.assetsFolder, statusCbk)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async write(session: FsSession, id: WebsiteId, files: ConnectorFile[], assetsFolder: string, statusCbk?: StatusCallback): Promise<void> {
|
|
204
|
+
const filesStatuses = this.initStatus(files)
|
|
205
|
+
let error: Error | null = null
|
|
206
|
+
for (const fileStatus of filesStatuses) {
|
|
207
|
+
const {file} = fileStatus
|
|
208
|
+
const path = join(this.options.path, id, assetsFolder, file.path)
|
|
209
|
+
if (typeof file.content === 'string' || Buffer.isBuffer(file.content)) {
|
|
210
|
+
fileStatus.message = 'Writing'
|
|
211
|
+
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
212
|
+
try {
|
|
213
|
+
await fs.writeFile(path, file.content, 'binary')
|
|
214
|
+
} catch(err) {
|
|
215
|
+
fileStatus.message = `Error (${err})`
|
|
216
|
+
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
217
|
+
error = err
|
|
218
|
+
continue
|
|
219
|
+
}
|
|
220
|
+
fileStatus.message = 'Success'
|
|
221
|
+
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
222
|
+
} else if (file.content instanceof Readable) {
|
|
223
|
+
fileStatus.message = 'Writing'
|
|
224
|
+
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
225
|
+
const writeStream = createWriteStream(path)
|
|
226
|
+
file.content.pipe(writeStream)
|
|
227
|
+
await new Promise((resolve) => {
|
|
228
|
+
writeStream.on('finish', () => {
|
|
229
|
+
fileStatus.message = 'Success'
|
|
230
|
+
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
231
|
+
resolve(file)
|
|
232
|
+
})
|
|
233
|
+
writeStream.on('error', err => {
|
|
234
|
+
console.error('writeStream error', err)
|
|
235
|
+
fileStatus.message = `Error (${err})`
|
|
236
|
+
this.updateStatus(filesStatuses, JobStatus.IN_PROGRESS, statusCbk)
|
|
237
|
+
error = err
|
|
238
|
+
resolve(file)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
} else {
|
|
242
|
+
console.error('Invalid file content', typeof file.content)
|
|
243
|
+
throw new Error('Invalid file content: ' + typeof file.content)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
this.updateStatus(filesStatuses, error ? JobStatus.ERROR : JobStatus.SUCCESS, statusCbk)
|
|
247
|
+
if(error) throw error
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async deleteAssets(session: FsSession, id: WebsiteId, paths: string[]): Promise<void> {
|
|
251
|
+
for (const path of paths) {
|
|
252
|
+
await fs.unlink(join(this.options.path, id, path))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async readAsset(session: object, websiteId: string, fileName: string): Promise<ConnectorFileContent> {
|
|
257
|
+
const id = requiredParam<WebsiteId>(websiteId, 'website id')
|
|
258
|
+
const path = join(this.options.path, id, this.options.assetsFolder, fileName)
|
|
259
|
+
return await fs.readFile(path)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
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 { Readable } from 'stream'
|
|
19
|
+
import { ServerConfig } from '../config'
|
|
20
|
+
import { JobStatus, JobData, ConnectorData, ConnectorId, WebsiteId, ConnectorType, ConnectorUser, WebsiteMeta, WebsiteMetaFileContent, WebsiteData, ConnectorOptions } from '../../types'
|
|
21
|
+
import { JobManager } from '../jobs'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @fileoverview define types for Silex connectors
|
|
25
|
+
* Bakends can provide storage for website data and assets, and/or hosting to publish the website online
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Connector data stored in the website meta file
|
|
30
|
+
*/
|
|
31
|
+
export type ConnectorFileContent = string | Buffer | Readable
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Files are stored in connector as a File object
|
|
35
|
+
* @see types/ClientSideFile
|
|
36
|
+
* @see contentToString()
|
|
37
|
+
*/
|
|
38
|
+
export interface ConnectorFile {
|
|
39
|
+
path: string,
|
|
40
|
+
content: ConnectorFileContent,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Callback to update the publication status
|
|
45
|
+
*/
|
|
46
|
+
export type StatusCallback = ({message, status}: {message: string, status: JobStatus}) => Promise<void>
|
|
47
|
+
|
|
48
|
+
export type ConnectorSession = object
|
|
49
|
+
/**
|
|
50
|
+
* Connectors are the base interface for Storage and Hosting connectors
|
|
51
|
+
*/
|
|
52
|
+
export interface Connector<Session extends ConnectorSession = ConnectorSession> {
|
|
53
|
+
connectorId: ConnectorId
|
|
54
|
+
connectorType: ConnectorType
|
|
55
|
+
displayName: string
|
|
56
|
+
icon: string
|
|
57
|
+
disableLogout?: boolean
|
|
58
|
+
color: string
|
|
59
|
+
background: string
|
|
60
|
+
// Get the URL to start the authentication process with OAuth (not used for basic auth)
|
|
61
|
+
getOAuthUrl(session: Session): Promise<string | null>
|
|
62
|
+
// Get the form to display to the user to authenticate (not used for OAuth)
|
|
63
|
+
getLoginForm(session: Session, redirectTo: string): Promise<string | null>
|
|
64
|
+
// Get the form to display to the user to set the connector settings for a given website
|
|
65
|
+
getSettingsForm(session: Session, redirectTo: string): Promise<string | null>
|
|
66
|
+
// Auth and user management
|
|
67
|
+
isLoggedIn(session: Session): Promise<boolean>
|
|
68
|
+
setToken(session: Session, token: object): Promise<void>
|
|
69
|
+
logout(session: Session): Promise<void>
|
|
70
|
+
getUser(session: Session): Promise<ConnectorUser | null>
|
|
71
|
+
// Get the connector options from login form
|
|
72
|
+
// They are stored in the website meta file for hosting connectors
|
|
73
|
+
// And in the user session for storage connectors
|
|
74
|
+
getOptions(formData: object): ConnectorOptions
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Storage are used to store the website data and assets
|
|
79
|
+
* And possibly rename files and directories, and get the URL of a file
|
|
80
|
+
*
|
|
81
|
+
*/
|
|
82
|
+
export interface StorageConnector<Session extends ConnectorSession = ConnectorSession> extends Connector<Session> {
|
|
83
|
+
// CRUD on websites
|
|
84
|
+
listWebsites(session: Session): Promise<WebsiteMeta[]>
|
|
85
|
+
readWebsite(session: Session, websiteId: WebsiteId): Promise<WebsiteData | Readable>
|
|
86
|
+
createWebsite(session: Session, data: WebsiteMetaFileContent): Promise<WebsiteId>
|
|
87
|
+
updateWebsite(session: Session, websiteId: WebsiteId, data: WebsiteData): Promise<void>
|
|
88
|
+
deleteWebsite(session: Session, websiteId: WebsiteId): Promise<void>
|
|
89
|
+
// CRUD on assets
|
|
90
|
+
writeAssets(session: Session, websiteId: WebsiteId, files: ConnectorFile[], status?: StatusCallback): Promise<string[] | void>
|
|
91
|
+
readAsset(session: Session, websiteId: WebsiteId, fileName: string): Promise<ConnectorFileContent>
|
|
92
|
+
deleteAssets(session: Session, websiteId: WebsiteId, fileNames: string[]): Promise<void>
|
|
93
|
+
//getFileUrl(session: Session, websiteId: WebsiteId, path: string): Promise<string>
|
|
94
|
+
// Handle website meta file
|
|
95
|
+
getWebsiteMeta(session: Session, websiteId: WebsiteId): Promise<WebsiteMeta>
|
|
96
|
+
setWebsiteMeta(session: Session, websiteId: WebsiteId, data: WebsiteMetaFileContent): Promise<void>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Hosting connectors are used to publish the website
|
|
101
|
+
*/
|
|
102
|
+
export interface HostingConnector<Session extends ConnectorSession = ConnectorSession> extends Connector<Session> {
|
|
103
|
+
publish(session: Session, websiteId: WebsiteId, files: ConnectorFile[], jobManager: JobManager): Promise<JobData> // Pass the jobManager as plugins do not neccessarily share the same module instance
|
|
104
|
+
getUrl(session: Session, websiteId: WebsiteId): Promise<string>
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function toConnectorEnum(type: string | ConnectorType): ConnectorType {
|
|
109
|
+
const result = ConnectorType[type.toString().toUpperCase() as keyof typeof ConnectorType]
|
|
110
|
+
if(!result) throw new Error(`Unknown connector type ${type}. It should be one of ${Object.keys(ConnectorType).join(', ')}`)
|
|
111
|
+
return result
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get a connector by id or by type
|
|
116
|
+
*/
|
|
117
|
+
export async function getConnector<T extends Connector>(config: ServerConfig, session: any, type: ConnectorType, connectorId?: ConnectorId): Promise<T | undefined> {
|
|
118
|
+
// Get the connectors for this type
|
|
119
|
+
const connectors = config.getConnectors(type) as T[]
|
|
120
|
+
|
|
121
|
+
// Find the connector by id
|
|
122
|
+
if (connectorId) return connectors.find(s => s.connectorId === connectorId && s.connectorType === type)
|
|
123
|
+
// Find the first logged in connector
|
|
124
|
+
for (const connector of connectors) {
|
|
125
|
+
if (await connector.isLoggedIn(session)) {
|
|
126
|
+
return connector
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Defaults to the first connector
|
|
130
|
+
return connectors[0]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Convert a connector to a ConnectorData object to be sent to the frontend
|
|
135
|
+
*/
|
|
136
|
+
export async function toConnectorData(session: any, connector: Connector): Promise<ConnectorData> {
|
|
137
|
+
return {
|
|
138
|
+
connectorId: connector.connectorId,
|
|
139
|
+
type: connector.connectorType,
|
|
140
|
+
displayName: connector.displayName,
|
|
141
|
+
icon: connector.icon,
|
|
142
|
+
disableLogout: !!connector.disableLogout,
|
|
143
|
+
isLoggedIn: await connector.isLoggedIn(session),
|
|
144
|
+
oauthUrl: await connector.getOAuthUrl(session),
|
|
145
|
+
color: connector.color,
|
|
146
|
+
background: connector.background,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function contentToString(content: ConnectorFileContent): Promise<string> {
|
|
151
|
+
// String
|
|
152
|
+
if (typeof content === 'string') return content
|
|
153
|
+
// Buffer
|
|
154
|
+
if (Buffer.isBuffer(content)) return content.toString('utf8')
|
|
155
|
+
// Stream
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
let result = ''
|
|
158
|
+
content.on('data', (chunk: Buffer) => {
|
|
159
|
+
result += chunk.toString('utf8')
|
|
160
|
+
})
|
|
161
|
+
content.on('error', (err: Error) => {
|
|
162
|
+
reject(err)
|
|
163
|
+
})
|
|
164
|
+
content.on('end', () => {
|
|
165
|
+
resolve(result)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function contentToBuffer(content: ConnectorFileContent): Promise<Buffer> {
|
|
171
|
+
// String
|
|
172
|
+
if (typeof content === 'string') return Buffer.from(content, 'utf8')
|
|
173
|
+
// Buffer
|
|
174
|
+
if (Buffer.isBuffer(content)) return content
|
|
175
|
+
// Stream
|
|
176
|
+
if(content instanceof Readable) {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
const chunks: Buffer[] = []
|
|
179
|
+
content.on('data', (chunk: Buffer) => {
|
|
180
|
+
chunks.push(chunk)
|
|
181
|
+
})
|
|
182
|
+
content.on('error', (err: Error) => {
|
|
183
|
+
reject(err)
|
|
184
|
+
})
|
|
185
|
+
content.on('end', () => {
|
|
186
|
+
resolve(Buffer.concat(chunks))
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
// Unknown
|
|
191
|
+
throw new Error('Unknown content type')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function contentToReadable(content: ConnectorFileContent): Readable {
|
|
195
|
+
// String
|
|
196
|
+
if (typeof content === 'string') return Readable.from([content])
|
|
197
|
+
// Buffer
|
|
198
|
+
if (Buffer.isBuffer(content)) return Readable.from([content])
|
|
199
|
+
// Stream
|
|
200
|
+
if (content instanceof Readable) return content
|
|
201
|
+
// Unknown
|
|
202
|
+
throw new Error('Unknown content type')
|
|
203
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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 { ConnectorId, PublicationData, WebsiteData, WebsiteId } from '../types'
|
|
19
|
+
import { ConnectorFile } from './connectors/connectors'
|
|
20
|
+
|
|
21
|
+
export enum ServerEvent {
|
|
22
|
+
STARTUP_START = 'startup-start',
|
|
23
|
+
STARTUP_END = 'startup-end',
|
|
24
|
+
PUBLISH_START = 'publish-start',
|
|
25
|
+
PUBLISH_END = 'publish-end',
|
|
26
|
+
WEBSITE_STORE_START = 'store-start',
|
|
27
|
+
WEBSITE_STORE_END = 'store-end',
|
|
28
|
+
WEBSITE_ASSET_STORE_START = 'asset-store-start',
|
|
29
|
+
WEBSITE_ASSET_STORE_END = 'asset-store-end',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type PublishStartEventType = PublicationData
|
|
33
|
+
export type PublishEndEventType = Error | null
|
|
34
|
+
export type WebsiteStoreStartEventType = { websiteId: WebsiteId, websiteData: WebsiteData, connectorId: ConnectorId}
|
|
35
|
+
export type WebsiteStoreEndEventType = Error | null
|
|
36
|
+
export type WebsiteAssetStoreStartEventType = { files: ConnectorFile[], websiteId: WebsiteId, connectorId: ConnectorId}
|
|
37
|
+
export type WebsiteAssetStoreEndEventType = Error | null
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
export * as events from './events'
|