@silexlabs/silex 3.0.1-alpha.0 → 3.0.2
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 +20 -0
- package/.silex.js +67 -8
- package/LICENSE +661 -203
- package/README.md +37 -35
- package/dist/client/client/api.d.ts +90 -0
- package/dist/client/client/assetUrl.d.ts +54 -0
- package/dist/client/client/config.d.ts +102 -0
- package/dist/client/client/events.d.ts +23 -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 +73 -0
- package/dist/client/client/grapesjs/PublicationUi.d.ts +59 -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/css-props.d.ts +6 -0
- package/dist/client/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/client/grapesjs/img.d.ts +6 -0
- package/dist/client/client/grapesjs/index.d.ts +9 -0
- package/dist/client/client/grapesjs/internal-links.d.ts +1 -0
- package/dist/client/client/grapesjs/keymaps.d.ts +123 -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 +9 -0
- package/dist/client/client/grapesjs/project-bar.d.ts +21 -0
- package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
- package/dist/client/client/grapesjs/rich-text.d.ts +4 -0
- package/dist/client/client/grapesjs/semantic.d.ts +1 -0
- package/dist/client/client/grapesjs/settings-sections.d.ts +16 -0
- package/dist/client/client/grapesjs/settings.d.ts +4 -0
- package/dist/client/client/grapesjs/storage.d.ts +2 -0
- package/dist/client/client/grapesjs/symbolDialogs.d.ts +3 -0
- package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
- package/dist/client/client/index.d.ts +10 -0
- package/dist/client/client/publication-transformers.d.ts +42 -0
- package/dist/client/client/publish-custom-code-block.d.ts +4 -0
- package/dist/client/client/publish-fonts.d.ts +4 -0
- package/dist/client/client/utils.d.ts +22 -0
- package/dist/client/constants.d.ts +32 -0
- package/dist/client/css/admin.css +1114 -328
- package/dist/client/css/admin.css.map +1 -0
- package/dist/client/grapesjs-filter-styles/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-fonts/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-fonts/src/commands.d.ts +3 -0
- package/dist/client/grapesjs-fonts/src/fonts.d.ts +4 -0
- package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +4 -0
- package/dist/client/index.html +3 -2
- package/dist/client/js/main.js +356 -131
- package/dist/client/js/main.js.LICENSE.txt +15 -1
- package/dist/client/page.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/api.d.ts +90 -0
- package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +54 -0
- package/dist/client/silex-lib/src/ts/client/config.d.ts +102 -0
- package/dist/client/silex-lib/src/ts/client/events.d.ts +23 -0
- package/dist/client/silex-lib/src/ts/client/expose.d.ts +8 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +15 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +73 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +59 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +6 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +6 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +123 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +21 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +16 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +3 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/index.d.ts +10 -0
- package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +42 -0
- package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/utils.d.ts +22 -0
- package/dist/client/silex-lib/src/ts/constants.d.ts +32 -0
- package/dist/client/silex-lib/src/ts/page.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/types.d.ts +339 -0
- package/dist/client/types.d.ts +339 -0
- package/dist/plugins/client/client/events.d.ts +23 -0
- package/dist/plugins/client/client/events.js +43 -0
- package/dist/plugins/client/client/events.js.map +1 -0
- package/dist/plugins/client/plugins/client/template.d.ts +2 -0
- package/dist/plugins/client/plugins/client/template.js +234 -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 +339 -0
- package/dist/plugins/client/types.js +80 -0
- package/dist/plugins/client/types.js.map +1 -0
- package/dist/plugins/server/constants.d.ts +32 -0
- package/dist/plugins/server/constants.js +76 -0
- package/dist/plugins/server/constants.js.map +1 -0
- package/dist/plugins/server/plugins/server/Cache.d.ts +2 -0
- package/dist/plugins/server/plugins/server/Cache.js +36 -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 +127 -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 +536 -0
- package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +135 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.js +649 -0
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +16 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +209 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/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 +8 -0
- package/dist/plugins/server/plugins/server/StaticPlugin.js +45 -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 +14 -0
- package/dist/plugins/server/server/connectors/FsHosting.js +66 -0
- package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -0
- package/dist/plugins/server/server/connectors/FsStorage.d.ts +44 -0
- package/dist/plugins/server/server/connectors/FsStorage.js +261 -0
- package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -0
- package/dist/plugins/server/server/connectors/connectors.d.ts +86 -0
- package/dist/plugins/server/server/connectors/connectors.js +126 -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 +79 -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 +37 -0
- package/dist/plugins/server/server/utils/validation.js.map +1 -0
- package/dist/plugins/server/types.d.ts +339 -0
- package/dist/plugins/server/types.js +84 -0
- package/dist/plugins/server/types.js.map +1 -0
- package/dist/server/constants.js +55 -0
- package/dist/server/plugins/server/Cache.js +19 -0
- package/dist/server/server/api/api.js +20 -0
- package/dist/server/server/api/connectorApi.js +334 -0
- package/dist/server/server/api/publicationApi.js +106 -0
- package/dist/server/server/api/websiteApi.js +315 -0
- package/dist/server/server/cli.js +71 -0
- package/dist/server/server/config.js +120 -0
- package/dist/server/server/connectors/FsHosting.js +47 -0
- package/dist/server/server/connectors/FsStorage.js +228 -0
- package/dist/server/server/connectors/connectors.js +88 -0
- package/dist/server/server/events.js +14 -0
- package/dist/server/server/expose.js +27 -0
- package/dist/server/server/express.js +46 -0
- package/dist/server/server/index.js +92 -0
- package/dist/server/server/jobs.js +50 -0
- package/dist/server/server/utils/validation.js +14 -0
- package/dist/server/types.js +40 -0
- package/package.json +99 -49
- package/public/assets/download.png +0 -0
- package/public/assets/favicon.ico +0 -0
- package/public/assets/favicon.png +0 -0
- package/public/assets/ftp.png +0 -0
- package/public/assets/gitlab.png +0 -0
- package/public/assets/gitlab2.png +0 -0
- package/public/assets/laptop.png +0 -0
- package/public/assets/logo-silex-small.png +0 -0
- package/public/silex.js +22 -23
- package/src/css/_variables.scss +24 -20
- package/src/css/dialog.scss +35 -2
- package/src/css/fonts.scss +31 -0
- package/src/css/footer.scss +12 -0
- package/src/css/grapesjs-light.scss +180 -0
- package/src/css/grapesjs-plugins.scss +227 -0
- package/src/css/grapesjs.scss +159 -1
- package/src/css/index.scss +12 -1
- package/src/css/light.scss +7 -0
- package/src/css/loader.scss +2 -0
- package/src/css/project-bar.scss +32 -16
- package/src/css/silex.scss +79 -5
- package/src/css/website-info.scss +14 -0
- package/src/html/index.pug +3 -3
- package/src/html/templates/includes.pug +0 -1
- package/src/ts/client/api.ts +156 -0
- package/src/ts/client/assetUrl.test.ts +164 -0
- package/src/ts/client/assetUrl.ts +254 -0
- package/src/ts/client/config.ts +175 -13
- package/src/ts/client/events.ts +43 -0
- package/src/ts/client/expose.ts +27 -1
- package/src/ts/client/grapesjs/LoginDialog.ts +204 -0
- package/src/ts/client/grapesjs/PublicationManager.ts +433 -0
- package/src/ts/client/grapesjs/PublicationUi.ts +366 -0
- package/src/ts/client/grapesjs/blocks.ts +17 -0
- package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
- package/src/ts/client/grapesjs/css-props.ts +910 -0
- package/src/ts/client/grapesjs/eleventy.ts +30 -7
- package/src/ts/client/grapesjs/footer.ts +42 -0
- package/src/ts/client/grapesjs/img.ts +147 -0
- package/src/ts/client/grapesjs/index.ts +311 -122
- package/src/ts/client/grapesjs/internal-links.test.ts +19 -1
- package/src/ts/client/grapesjs/internal-links.ts +31 -7
- package/src/ts/client/grapesjs/keymaps.ts +207 -0
- package/src/ts/client/grapesjs/new-page-dialog.ts +19 -3
- package/src/ts/client/grapesjs/openImport.ts +22 -0
- package/src/ts/client/grapesjs/page-panel.ts +109 -7
- package/src/ts/client/grapesjs/project-bar.ts +133 -72
- package/src/ts/client/grapesjs/resize-panel.ts +20 -0
- package/src/ts/client/grapesjs/rich-text.ts +38 -13
- package/src/ts/client/grapesjs/semantic.ts +20 -3
- package/src/ts/client/grapesjs/settings-sections.ts +117 -0
- package/src/ts/client/grapesjs/settings.ts +191 -96
- package/src/ts/client/grapesjs/storage.ts +187 -0
- package/src/ts/client/grapesjs/symbolDialogs.ts +21 -5
- package/src/ts/client/grapesjs/view-buttons.ts +42 -0
- package/src/ts/client/index.ts +80 -21
- package/src/ts/client/publication-transformers.test.ts +207 -0
- package/src/ts/client/publication-transformers.ts +280 -0
- package/src/ts/client/publish-custom-code-block.ts +31 -0
- package/src/ts/client/publish-fonts.ts +36 -0
- package/src/ts/client/utils.ts +70 -1
- package/src/ts/constants.ts +78 -0
- package/src/ts/externs.ts +17 -0
- package/src/ts/page.ts +20 -3
- package/src/ts/plugins/client/template.ts +246 -0
- package/src/ts/plugins/client/website-info.ts +84 -0
- package/src/ts/plugins/server/Cache.ts +32 -0
- package/src/ts/plugins/server/DownloadConnector.ts +146 -0
- package/src/ts/plugins/server/FtpConnector.test.ts +82 -0
- package/src/ts/plugins/server/FtpConnector.ts +621 -0
- package/src/ts/plugins/server/GitlabConnector.ts +749 -0
- package/src/ts/plugins/server/GitlabHostingConnector.ts +215 -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 +45 -0
- package/src/ts/server/api/api.ts +19 -0
- package/src/ts/server/api/connectorApi.ts +464 -0
- package/src/ts/server/api/publicationApi.ts +159 -0
- package/src/ts/server/api/websiteApi.ts +462 -0
- package/src/ts/server/cli.ts +48 -14
- package/src/ts/server/config.ts +158 -60
- package/src/ts/server/connectors/FsHosting.ts +66 -0
- package/src/ts/server/connectors/FsStorage.test.ts +51 -0
- package/src/ts/server/connectors/FsStorage.ts +290 -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 +57 -5
- package/src/ts/server/index.ts +80 -19
- package/src/ts/server/jobs.test.ts +75 -0
- package/src/ts/server/jobs.ts +89 -0
- package/src/ts/server/utils/BackwardCompat.ts +546 -528
- package/src/ts/server/utils/validation.ts +34 -0
- package/src/ts/types.ts +295 -85
- package/.dockerignore +0 -5
- package/.editorconfig +0 -14
- package/.eslintrc.json +0 -43
- package/.github/FUNDING.yml +0 -13
- package/.github/workflows/docker-hub-publish.yml +0 -36
- package/.github/workflows/npm-publish.yml +0 -19
- package/.github/workflows/test.yml +0 -26
- package/.nvmrc +0 -1
- package/.silex/default/.silex.data.json +0 -1
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -131
- package/Dockerfile +0 -15
- package/directus.silex.js +0 -7
- package/dist/constants.js +0 -4
- package/dist/events.js +0 -5
- package/dist/page.js +0 -15
- package/dist/server/cli.js +0 -43
- package/dist/server/config.js +0 -70
- package/dist/server/express.js +0 -18
- package/dist/server/index.js +0 -58
- package/dist/types.js +0 -34
- package/jest.config.js +0 -10
- package/logos/logo_by_ceubri/silex-icon-2018@200px.png +0 -0
- package/logos/logo_by_ceubri/silex-icon-2018@4x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white-transp.pdf +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white.pdf +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@0.5x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@0.75x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@4x.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018-white@4x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018.ai +5 -2521
- package/logos/logo_by_ceubri/silex-logo-2018.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018.pdf +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@0.5x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@0.75x.png +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@4x.jpg +0 -0
- package/logos/logo_by_ceubri/silex-logo-2018@4x.png +0 -0
- package/logos/silexlabs.png +0 -0
- package/src/example-client-config.js +0 -47
- package/src/plugins/AuthPlugin.js +0 -21
- package/src/plugins/Cache.js +0 -11
- package/src/plugins/DefaultBackend.js +0 -149
- package/src/plugins/ExpressPlugin.js +0 -39
- package/src/plugins/HooksPlugin.js +0 -58
- package/src/plugins/PublishPlugin.js +0 -69
- package/src/plugins/SslPlugin.js +0 -66
- package/src/plugins/StaticPlugin.js +0 -48
- package/src/plugins/WebsitePlugin.js +0 -207
- package/src/ts/client/grapesjs/publish.ts +0 -386
- package/src/ts/client/grapesjs/template.ts +0 -166
- package/src/ts/events.ts +0 -2
- package/tsconfig-server-debug.json +0 -19
- package/tsconfig-server-release.json +0 -19
- package/tsconfig.jest.json +0 -9
- package/tsconfig.json +0 -26
- package/webpack.config.js +0 -28
- package/webpack.debug.config.js +0 -8
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { Request, Response, Router } from 'express'
|
|
2
|
+
import { API_CONNECTOR_LIST, API_CONNECTOR_LOGIN_CALLBACK, API_CONNECTOR_USER, API_CONNECTOR_LOGOUT, API_CONNECTOR_LOGIN, API_CONNECTOR_SETTINGS, API_PATH, API_CONNECTOR_PATH } from '../../constants'
|
|
3
|
+
import { ServerConfig } from '../config'
|
|
4
|
+
import { requiredParam } from '../utils/validation'
|
|
5
|
+
import { Connector, getConnector, toConnectorData, toConnectorEnum } from '../connectors/connectors'
|
|
6
|
+
import { ApiConnectorListQuery, ApiConnectorListResponse, ApiConnectorLoggedInPostMessage, ApiConnectorLoginQuery, ApiConnectorUserQuery, ApiConnectorUserResponse, ApiConnectorLogoutQuery, ConnectorId, ApiConnectorLoginCbkQuery, ApiConnectorLoginCbkBody, ConnectorOptions, ApiResponseError, ConnectorType } from '../../types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @fileoverview The connector API adds routes to handle the connectors and the methods they implement, this includes authentication and user data.
|
|
10
|
+
*
|
|
11
|
+
* About authentication
|
|
12
|
+
*
|
|
13
|
+
* There are 2 types of connectors, for storage of website data and for publication to a hosting service. The authentication process is the same for both types of connectors
|
|
14
|
+
*
|
|
15
|
+
* Here is a typical authentication flow, which can be for a storage or a publication connector:
|
|
16
|
+
*
|
|
17
|
+
* 1. Client app calls the `user` route which returns a 401 as there is no auth in the session
|
|
18
|
+
* 1. Client app lists all connectors (either for publication or storage) and display a button for each connector
|
|
19
|
+
* 1. The user clicks a button, it opens a page with either the connector's `getOAuthUrl` for OAuth connectors (A) or the login route for basic auth connectors (B). This can be done in a popup (C) or as a redirect in the client app window (D)
|
|
20
|
+
* 1. OAuth (A): the page displays the OAuth page which ends up going back to the callback page
|
|
21
|
+
* 1. Basic auth (B): the page displays the login form `connector.getLoginForm()` which ends up going back to the callback page
|
|
22
|
+
* 1. The callback page stores the auth data in the session (for storage), or in the website publication settings (for publication)
|
|
23
|
+
* 1. Popup (C): The callback page will post a message to the parent window which closes the popup and goes on with the flow
|
|
24
|
+
* 1. Redirect (D): The callback page will redirect to the client app
|
|
25
|
+
* 1. The client side will call the `user` route again, which will return the user data
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Add routes to the express app
|
|
30
|
+
*/
|
|
31
|
+
export default function(config: ServerConfig) {
|
|
32
|
+
// Create the router
|
|
33
|
+
const router = Router()
|
|
34
|
+
|
|
35
|
+
// Connector routes
|
|
36
|
+
router.get(API_CONNECTOR_USER, routeUser)
|
|
37
|
+
router.get(API_CONNECTOR_LIST, routeListConnectors)
|
|
38
|
+
router.get(API_CONNECTOR_LOGIN, routeLogin)
|
|
39
|
+
// router.get(API_CONNECTOR_SETTINGS, routeSettings)
|
|
40
|
+
// router.post(API_CONNECTOR_SETTINGS, routeSettingsPost)
|
|
41
|
+
router.post(API_CONNECTOR_LOGIN_CALLBACK, routeLoginSuccess)
|
|
42
|
+
router.get(API_CONNECTOR_LOGIN_CALLBACK, routeLoginSuccess)
|
|
43
|
+
router.post(API_CONNECTOR_LOGOUT, routeLogout)
|
|
44
|
+
|
|
45
|
+
return router
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Method to validate HTTP status codes
|
|
50
|
+
* Returns a suitable error code if it is not valid
|
|
51
|
+
*/
|
|
52
|
+
function validateStatus(status: number, _default = 500): number {
|
|
53
|
+
if(!status) {
|
|
54
|
+
console.warn(`Status code is undefined, returning default ${_default}`)
|
|
55
|
+
return _default
|
|
56
|
+
}
|
|
57
|
+
// Make sure it is a string
|
|
58
|
+
status = parseInt(status.toString())
|
|
59
|
+
// Check the status code
|
|
60
|
+
if (status >= 100 && status < 600) return status
|
|
61
|
+
// Invalid status code
|
|
62
|
+
console.warn(`Invalid status code ${status}, returning default ${_default}}`)
|
|
63
|
+
return _default
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Express route to check if the user is logged in
|
|
68
|
+
* Returns user data and connector data
|
|
69
|
+
*/
|
|
70
|
+
async function routeUser(req: Request, res: Response) {
|
|
71
|
+
try {
|
|
72
|
+
const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
73
|
+
const session = requiredParam(req['session'], 'Session object')
|
|
74
|
+
const query = req.query as ApiConnectorUserQuery
|
|
75
|
+
const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
76
|
+
const connector = await getConnector<Connector>(config, session, type, query.connectorId)
|
|
77
|
+
if (!connector) {
|
|
78
|
+
res
|
|
79
|
+
.status(500)
|
|
80
|
+
.json({
|
|
81
|
+
error: true,
|
|
82
|
+
message: `Connector not found: ${type} ${query.connectorId}`,
|
|
83
|
+
} as ApiResponseError)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
if(!await connector.isLoggedIn(session)) {
|
|
87
|
+
res
|
|
88
|
+
.status(401)
|
|
89
|
+
.json({
|
|
90
|
+
error: true,
|
|
91
|
+
message: 'Not logged in',
|
|
92
|
+
} as ApiResponseError)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
// User logged in, return user data
|
|
96
|
+
const user = await connector.getUser(session)
|
|
97
|
+
res.json(user as ApiConnectorUserResponse)
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Error in the user request', error, error.code)
|
|
100
|
+
res.status(validateStatus(error.code ?? error.httpStatusCode, 500)).json({
|
|
101
|
+
error: true,
|
|
102
|
+
message: error.message,
|
|
103
|
+
} as ApiResponseError)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Express route to list the connectors
|
|
109
|
+
*/
|
|
110
|
+
async function routeListConnectors(req: Request, res: Response) {
|
|
111
|
+
try {
|
|
112
|
+
const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
113
|
+
const session = requiredParam(req['session'], 'Session object')
|
|
114
|
+
const query = req.query as ApiConnectorListQuery
|
|
115
|
+
const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
116
|
+
const connectors = config.getConnectors(type)
|
|
117
|
+
try {
|
|
118
|
+
const list = await Promise.all(connectors.map(async connector => toConnectorData(session, connector)))
|
|
119
|
+
res.json(list as ApiConnectorListResponse)
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Error while listing connectors', error)
|
|
122
|
+
res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
|
|
123
|
+
error: true,
|
|
124
|
+
message: 'Error while listing connectors: ' + error.message,
|
|
125
|
+
} as ApiResponseError)
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error('Error in the list connectors request', error)
|
|
129
|
+
res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
|
|
130
|
+
error: true,
|
|
131
|
+
message: 'Error in the list connectors request: ' + error.message,
|
|
132
|
+
} as ApiResponseError)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Route login
|
|
138
|
+
* Display the connector's login form if the connector is basic auth
|
|
139
|
+
* or redirect to oauth url if the connector is oauth
|
|
140
|
+
* or redirect to success page if the user is logged in
|
|
141
|
+
*/
|
|
142
|
+
async function routeLogin(req: Request, res: Response) {
|
|
143
|
+
try {
|
|
144
|
+
const query = req.query as ApiConnectorLoginQuery
|
|
145
|
+
const connectorId = requiredParam(query.connectorId, 'Connector id')
|
|
146
|
+
const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
147
|
+
const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
148
|
+
const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
|
|
149
|
+
const session = requiredParam(req['session'], 'Session object')
|
|
150
|
+
if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
|
|
151
|
+
// Check if the user is already logged in
|
|
152
|
+
if (await connector.isLoggedIn(session)) {
|
|
153
|
+
res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`)
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
const oauthUrl = await connector.getOAuthUrl(session)
|
|
157
|
+
if (oauthUrl) {
|
|
158
|
+
// Starts the OAuth flow
|
|
159
|
+
res.redirect(oauthUrl)
|
|
160
|
+
} else {
|
|
161
|
+
// Display the login form
|
|
162
|
+
const redirectTo = await connector.getSettingsForm(session, '') ?
|
|
163
|
+
`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_SETTINGS}?connectorId=${connectorId}&type=${type}` :
|
|
164
|
+
`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}`
|
|
165
|
+
res.send(await connector.getLoginForm(session, redirectTo))
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error('Error in the login request', error)
|
|
169
|
+
res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
|
|
170
|
+
error: true,
|
|
171
|
+
message: 'Error in the login request: ' + error.message,
|
|
172
|
+
} as ApiResponseError)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// /**
|
|
177
|
+
// * Route connector settings
|
|
178
|
+
// * Display the connector's settings form or redirect to login page if the user is not logged in
|
|
179
|
+
// */
|
|
180
|
+
// function routeSettings(req: Request, res: Response) {
|
|
181
|
+
// try {
|
|
182
|
+
// const query = req.query as ApiConnectorSettingsQuery
|
|
183
|
+
//
|
|
184
|
+
// const connectorId = requiredParam(query.connectorId, 'Connector id')
|
|
185
|
+
// const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
186
|
+
// const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
187
|
+
// const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
|
|
188
|
+
// const session = requiredParam(req['session'], 'Session object')
|
|
189
|
+
// if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
|
|
190
|
+
// // Check if the user is already logged in
|
|
191
|
+
// if (!await connector.isLoggedIn(session)) {
|
|
192
|
+
// res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
|
|
193
|
+
// return
|
|
194
|
+
// }
|
|
195
|
+
// // Display the login form
|
|
196
|
+
// res.send(await connector.getSettingsForm(session, ``) as ApiConnectorSettingsResponse)
|
|
197
|
+
// } catch (error) {
|
|
198
|
+
// console.error('Error in the login request', error)
|
|
199
|
+
// res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
|
|
200
|
+
// error: true,
|
|
201
|
+
// message: 'Error in the login request: ' + error.message,
|
|
202
|
+
// } as ApiError)
|
|
203
|
+
// }
|
|
204
|
+
// }
|
|
205
|
+
//
|
|
206
|
+
// /**
|
|
207
|
+
// * Route connector settings post
|
|
208
|
+
// * Save the connector's settings in the website meta file
|
|
209
|
+
// */
|
|
210
|
+
// function routeSettingsPost(req: Request, res: Response) {
|
|
211
|
+
// try {
|
|
212
|
+
// const query = req.query as ApiConnectorSettingsPostQuery
|
|
213
|
+
//
|
|
214
|
+
// const connectorId = requiredParam(query.connectorId, 'Connector id')
|
|
215
|
+
// const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
216
|
+
// const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
217
|
+
// const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
|
|
218
|
+
// const session = requiredParam(req['session'], 'Session object')
|
|
219
|
+
// const body = req.body as ApiConnectorSettingsPostBody
|
|
220
|
+
// if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
|
|
221
|
+
// // Check if the user is already logged in
|
|
222
|
+
// if (!await connector.isLoggedIn(session)) {
|
|
223
|
+
// res.redirect(`${config.url}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN_CALLBACK}?connectorId=${connectorId}&type=${type}&error=Not logged in`)
|
|
224
|
+
// return
|
|
225
|
+
// }
|
|
226
|
+
// // Save the settings
|
|
227
|
+
// await connector.setWebsiteMeta(session, websiteId, body)
|
|
228
|
+
// } catch (error) {
|
|
229
|
+
// console.error('Error in the login request', error)
|
|
230
|
+
// res.status(error?.code ?? error?.httpStatusCode ?? 400).json({
|
|
231
|
+
// error: true,
|
|
232
|
+
// message: 'Error in the login request: ' + error.message,
|
|
233
|
+
// } as ApiResponseError)
|
|
234
|
+
// }
|
|
235
|
+
// }
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Express route to serve as redirect after a successful login
|
|
239
|
+
* The returned HTML will postMessage data and close the popup window
|
|
240
|
+
*/
|
|
241
|
+
async function routeLoginSuccess(req: Request, res: Response) {
|
|
242
|
+
try {
|
|
243
|
+
const query = req.query as ApiConnectorLoginCbkQuery
|
|
244
|
+
if(query.error) throw new Error(query.error)
|
|
245
|
+
|
|
246
|
+
const body = req.body as ApiConnectorLoginCbkBody
|
|
247
|
+
const session = requiredParam(req['session'], 'Session object')
|
|
248
|
+
|
|
249
|
+
const connectorId = requiredParam(query.connectorId, 'Connector id')
|
|
250
|
+
const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
251
|
+
const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
252
|
+
const connector = await getConnector<Connector>(config, req['session'], type, connectorId)
|
|
253
|
+
if (!connector) throw new Error('Connector not found ' + connectorId)
|
|
254
|
+
// Check if the user is already logged in
|
|
255
|
+
if (await connector.isLoggedIn(session)) {
|
|
256
|
+
console.info('User already logged in for connector ' + connectorId)
|
|
257
|
+
} else {
|
|
258
|
+
// Store the auth info in the session
|
|
259
|
+
// This is useful for storage only
|
|
260
|
+
await connector.setToken(session, {
|
|
261
|
+
...query,
|
|
262
|
+
...body,
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
// End the auth flow
|
|
266
|
+
res.send(getEndAuthHtml('Logged in', false, connectorId, type, connector.getOptions(body)))
|
|
267
|
+
} catch (error) {
|
|
268
|
+
res
|
|
269
|
+
.status(validateStatus(error?.code ?? error?.httpStatusCode, 500))
|
|
270
|
+
.send(getEndAuthHtml(error.message, true, req.query.connectorId as ConnectorId, req.query.type as ConnectorType))
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Express route to logout from a connector
|
|
276
|
+
*/
|
|
277
|
+
async function routeLogout(req: Request, res: Response) {
|
|
278
|
+
try {
|
|
279
|
+
const query = req.query as ApiConnectorLogoutQuery
|
|
280
|
+
const session = requiredParam(req['session'], 'Session object')
|
|
281
|
+
// Get the connector
|
|
282
|
+
const config = requiredParam(req.app.get('config') as ServerConfig, 'Config object on express js APP')
|
|
283
|
+
const type = toConnectorEnum(requiredParam(query.type, 'Connector type'))
|
|
284
|
+
const connectorId = query.connectorId
|
|
285
|
+
const connector = await getConnector<Connector>(config, session, type, connectorId)
|
|
286
|
+
if (!connector) throw new Error(`Connector not found ${connectorId} ${type}`)
|
|
287
|
+
try {
|
|
288
|
+
// Logout
|
|
289
|
+
await connector.logout(session)
|
|
290
|
+
// Return success
|
|
291
|
+
res.json({
|
|
292
|
+
error: false,
|
|
293
|
+
message: 'OK',
|
|
294
|
+
} as ApiResponseError)
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error('Error while logging out', error)
|
|
297
|
+
res.status(validateStatus(error?.code ?? error?.httpStatusCode, 500)).json({
|
|
298
|
+
error: true,
|
|
299
|
+
message: 'Error while logging out: ' + error.message,
|
|
300
|
+
} as ApiResponseError)
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error('Error in the logout request', error)
|
|
305
|
+
res.status(validateStatus(error?.code ?? error?.httpStatusCode, 400)).json({
|
|
306
|
+
error: true,
|
|
307
|
+
message: 'Error in the logout request: ' + error.message,
|
|
308
|
+
} as ApiResponseError)
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Utility function to send an HTML page to the browser
|
|
315
|
+
* is page will send a postMessage to the parent window and close itself
|
|
316
|
+
*/
|
|
317
|
+
function getEndAuthHtml(message: string, error: boolean, connectorId: ConnectorId, connectorType: ConnectorType, options?: ConnectorOptions): string {
|
|
318
|
+
// Data for postMessage
|
|
319
|
+
const data = {
|
|
320
|
+
type: 'login', // For postMessage
|
|
321
|
+
error,
|
|
322
|
+
message,
|
|
323
|
+
connectorId,
|
|
324
|
+
connectorType,
|
|
325
|
+
options,
|
|
326
|
+
} as ApiConnectorLoggedInPostMessage
|
|
327
|
+
// Determine status title and heading based on the error
|
|
328
|
+
const status = error ? 'Error' : 'Success'
|
|
329
|
+
// Return the HTML template
|
|
330
|
+
return `
|
|
331
|
+
<!DOCTYPE html><html lang="en">
|
|
332
|
+
<head>
|
|
333
|
+
<title>Authentication ${status}</title>
|
|
334
|
+
<style>
|
|
335
|
+
:root {
|
|
336
|
+
--primaryColor: #333333;
|
|
337
|
+
--secondaryColor: #ddd;
|
|
338
|
+
--tertiaryColor: #8873FE;
|
|
339
|
+
--quaternaryColor: #A291FF;
|
|
340
|
+
--darkerPrimaryColor: #292929;
|
|
341
|
+
--lighterPrimaryColor: #575757;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
body {
|
|
345
|
+
font-family: Arial, sans-serif;
|
|
346
|
+
text-align: center;
|
|
347
|
+
margin: 50px;
|
|
348
|
+
color: var(--primaryColor);
|
|
349
|
+
background-color: var(--secondaryColor);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
h1 {
|
|
353
|
+
color: var(--tertiaryColor);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
p {
|
|
357
|
+
font-size: 18px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
a {
|
|
361
|
+
color: var(--tertiaryColor);
|
|
362
|
+
text-decoration: none;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
a:hover {
|
|
366
|
+
text-decoration: underline;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.container {
|
|
370
|
+
max-width: 600px;
|
|
371
|
+
margin: auto;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.button {
|
|
375
|
+
display: inline-block;
|
|
376
|
+
margin-top: 20px;
|
|
377
|
+
padding: 10px 20px;
|
|
378
|
+
font-size: 16px;
|
|
379
|
+
color: var(--secondaryColor);
|
|
380
|
+
background-color: var(--tertiaryColor);
|
|
381
|
+
border: none;
|
|
382
|
+
border-radius: 5px;
|
|
383
|
+
text-decoration: none;
|
|
384
|
+
cursor: pointer;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.button:hover {
|
|
388
|
+
background-color: var(--quaternaryColor); /* Slightly lighter shade of tertiary color */
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.error {
|
|
392
|
+
display: none;
|
|
393
|
+
margin-top: 20px;
|
|
394
|
+
padding: 15px;
|
|
395
|
+
border: 1px solid var(--tertiaryColor);
|
|
396
|
+
border-radius: 5px;
|
|
397
|
+
text-wrap: wrap;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
</style>
|
|
401
|
+
</head>
|
|
402
|
+
<body>
|
|
403
|
+
<div class="container">
|
|
404
|
+
<div id="message">
|
|
405
|
+
<h1>Authentication ${status}</h1>
|
|
406
|
+
<p>${error ? '' : message}</p>
|
|
407
|
+
</div>
|
|
408
|
+
${ error ? `
|
|
409
|
+
<p><a data-link-to href="/">click here to continue</a>.</p>
|
|
410
|
+
<a data-link-to href="/" class="button">Retry</a>
|
|
411
|
+
<pre id="error-container" class="error">${message}</pre>
|
|
412
|
+
<script>
|
|
413
|
+
document.getElementById('error-container').style.display = "block";
|
|
414
|
+
fetch("${ API_PATH }${ API_CONNECTOR_PATH }${ API_CONNECTOR_LOGOUT }?connectorId=${ connectorId }&type=${ connectorType }", {
|
|
415
|
+
method: 'POST',
|
|
416
|
+
headers: {
|
|
417
|
+
'Content-Type': 'application/json',
|
|
418
|
+
credentials: 'include', // sends the cookies with the request
|
|
419
|
+
},
|
|
420
|
+
})
|
|
421
|
+
if(window.opener && window.opener !== window) {
|
|
422
|
+
const linksTo = document.querySelectorAll('[data-link-to]');
|
|
423
|
+
linksTo.forEach(link => {
|
|
424
|
+
link.addEventListener('click', (e) => {
|
|
425
|
+
e.preventDefault();
|
|
426
|
+
const data = ${JSON.stringify(data)};
|
|
427
|
+
window.opener.postMessage(data, '*');
|
|
428
|
+
window.close();
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
</script>
|
|
433
|
+
` : `
|
|
434
|
+
<p>If this window doesn’t close automatically, <a href="/">click here to return to the homepage</a>.</p>
|
|
435
|
+
<a href="/" class="button">Go to Homepage</a>
|
|
436
|
+
<pre id="error-container" class="error"></pre>
|
|
437
|
+
<script>
|
|
438
|
+
const data = ${JSON.stringify(data)};
|
|
439
|
+
const errorContainer = document.getElementById('error-container');
|
|
440
|
+
const messageContainer = document.getElementById('message');
|
|
441
|
+
|
|
442
|
+
// Check if the window was opened programmatically and is valid
|
|
443
|
+
if (window.opener && window.opener !== window) {
|
|
444
|
+
try {
|
|
445
|
+
// Send a postMessage to the opener
|
|
446
|
+
window.opener.postMessage(data, '*');
|
|
447
|
+
// Attempt to close the window
|
|
448
|
+
window.close();
|
|
449
|
+
} catch (e) {
|
|
450
|
+
// Display error message if closing fails
|
|
451
|
+
errorContainer.innerText = "Unable to close the window. Please close it manually.";
|
|
452
|
+
errorContainer.style.display = "block";
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
messageContainer.innerHTML = '<h1>Redirecting, please wait...</h1>';
|
|
456
|
+
window.location.href = '/'
|
|
457
|
+
}
|
|
458
|
+
</script>
|
|
459
|
+
`}
|
|
460
|
+
</div>
|
|
461
|
+
</body>
|
|
462
|
+
</html>
|
|
463
|
+
`
|
|
464
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
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 { API_PUBLICATION_PUBLISH, API_PUBLICATION_STATUS, API_WEBSITE_ASSET_READ } from '../../constants'
|
|
20
|
+
import { ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, JobId, PublicationData, ConnectorType, ConnectorId, WebsiteId, ClientSideFile, ClientSideFileWithContent, ClientSideFileWithSrc} from '../../types'
|
|
21
|
+
import { ConnectorFile, HostingConnector, StorageConnector, getConnector } from '../connectors/connectors'
|
|
22
|
+
import { ServerConfig } from '../config'
|
|
23
|
+
import { requiredParam } from '../utils/validation'
|
|
24
|
+
import { PublishEndEventType, PublishStartEventType, ServerEvent } from '../events'
|
|
25
|
+
import { getJob, jobManager } from '../jobs'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @fileoverview Publication plugin for Silex
|
|
29
|
+
* Adds a publication API to Silex server
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Error thrown by the publication API
|
|
34
|
+
* @param message error message
|
|
35
|
+
* @param code http status code
|
|
36
|
+
*/
|
|
37
|
+
export class PublicationError extends Error {
|
|
38
|
+
constructor(message: string, public code: number) {
|
|
39
|
+
super(message)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const PROJECT_ROOT = require.main ? require.main.path : process.cwd()
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the desired connector
|
|
47
|
+
* Can be the default connector or a specific one
|
|
48
|
+
*/
|
|
49
|
+
export async function getHostingConnector(session: any, config: ServerConfig, connectorId?: string): Promise<HostingConnector> {
|
|
50
|
+
const hostingConnector = await getConnector(config, session, ConnectorType.HOSTING, connectorId) // ?? config.getHostingConnectors()[0]
|
|
51
|
+
|
|
52
|
+
if (!hostingConnector) {
|
|
53
|
+
throw new PublicationError('No hosting connector found', 500)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!await hostingConnector.isLoggedIn(session)) {
|
|
57
|
+
throw new PublicationError('Not logged in', 401)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return hostingConnector as HostingConnector
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function getStorageConnector(session: any, config: ServerConfig, connectorId?: string): Promise<StorageConnector> {
|
|
64
|
+
const storageConnector = await getConnector(config, session, ConnectorType.STORAGE, connectorId) // ?? config.getStorageConnectors()[0]
|
|
65
|
+
|
|
66
|
+
if (!storageConnector) {
|
|
67
|
+
throw new PublicationError('No storage connector found', 500)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!await storageConnector.isLoggedIn(session)) {
|
|
71
|
+
throw new PublicationError('Not logged in to the storage connector', 401)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return storageConnector as StorageConnector
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default function (config: ServerConfig): Router {
|
|
78
|
+
// Create a new router
|
|
79
|
+
const router = Router()
|
|
80
|
+
|
|
81
|
+
// Get publication status
|
|
82
|
+
router.get(API_PUBLICATION_STATUS, async function (req, res) {
|
|
83
|
+
const query: ApiPublicationStatusQuery = req.query as any
|
|
84
|
+
const jobId: JobId = query.jobId as string
|
|
85
|
+
const job = jobId && getJob(jobId)
|
|
86
|
+
if (!job) {
|
|
87
|
+
console.error(`Error: job not found with id ${jobId}`)
|
|
88
|
+
res.status(404).json({
|
|
89
|
+
message: 'Error: job not found.',
|
|
90
|
+
})
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
res.json({
|
|
94
|
+
...job,
|
|
95
|
+
_timeout: undefined,
|
|
96
|
+
} as ApiPublicationStatusResponse)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Publish website
|
|
100
|
+
router.post(API_PUBLICATION_PUBLISH, async function (req, res) {
|
|
101
|
+
try {
|
|
102
|
+
const query: ApiPublicationPublishQuery = req.query as any
|
|
103
|
+
const body: ApiPublicationPublishBody = req.body
|
|
104
|
+
const websiteId = requiredParam<WebsiteId>(query.websiteId as string, 'id in query')
|
|
105
|
+
const storageId = requiredParam<ConnectorId>(query.storageId as string, 'storageId in query')
|
|
106
|
+
const hostingId = requiredParam<ConnectorId>(query.hostingId as string, 'hostingId in query')
|
|
107
|
+
const session = requiredParam(req['session'], 'session on express request')
|
|
108
|
+
|
|
109
|
+
// Check params
|
|
110
|
+
const data = body as PublicationData
|
|
111
|
+
const { files } = data
|
|
112
|
+
if(!files) {
|
|
113
|
+
throw new PublicationError('Missing files in body', 400)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Hook for plugins
|
|
117
|
+
config.emit(ServerEvent.PUBLISH_START, data as PublishStartEventType)
|
|
118
|
+
|
|
119
|
+
// Get hosting connector and make sure the user is logged in
|
|
120
|
+
const hostingConnector = await getHostingConnector(session, config, hostingId)
|
|
121
|
+
|
|
122
|
+
// Get storage connector which holds the assets
|
|
123
|
+
const storage = await getStorageConnector(session, config, storageId)
|
|
124
|
+
|
|
125
|
+
// Load the content if necessary
|
|
126
|
+
const filesList: ConnectorFile[] = await Promise.all(files.map(async (file: ClientSideFile) => {
|
|
127
|
+
const fileWithContent = file as ClientSideFileWithContent
|
|
128
|
+
const fileWithSrc = file as ClientSideFileWithSrc
|
|
129
|
+
if(!fileWithContent.content && !fileWithSrc.src) throw new PublicationError('Missing content or src in file', 400)
|
|
130
|
+
const src = fileWithSrc.src
|
|
131
|
+
return {
|
|
132
|
+
// Destination
|
|
133
|
+
path: file.permalink ?? file.path,
|
|
134
|
+
// Content
|
|
135
|
+
content: fileWithContent.content ?? await storage.readAsset(session, websiteId, fileWithSrc.src),
|
|
136
|
+
}
|
|
137
|
+
}))
|
|
138
|
+
|
|
139
|
+
res.json({
|
|
140
|
+
url: await hostingConnector.getUrl(session, websiteId),
|
|
141
|
+
job: await hostingConnector.publish(session, websiteId, filesList, jobManager),
|
|
142
|
+
} as ApiPublicationPublishResponse)
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error('Error publishing the website', err)
|
|
145
|
+
res
|
|
146
|
+
.status(typeof err.code === 'number' ? err.code : 500)
|
|
147
|
+
.json({
|
|
148
|
+
message: `Error publishing the website. ${err.message}`
|
|
149
|
+
})
|
|
150
|
+
// Hook for plugins
|
|
151
|
+
config.emit(ServerEvent.PUBLISH_END, err as PublishEndEventType)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
// Hook for plugins
|
|
155
|
+
config.emit(ServerEvent.PUBLISH_END)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
return router
|
|
159
|
+
}
|