@silexlabs/silex 3.0.1-0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.default +3 -2
- package/.silex.js +39 -1
- package/README.md +37 -35
- package/dist/client/client/api.d.ts +8 -1
- package/dist/client/client/assetUrl.d.ts +54 -0
- package/dist/client/client/config.d.ts +60 -1
- package/dist/client/client/events.d.ts +19 -10
- package/dist/client/client/grapesjs/PublicationManager.d.ts +6 -8
- package/dist/client/client/grapesjs/PublicationUi.d.ts +8 -10
- package/dist/client/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/client/client/grapesjs/css-props.d.ts +6 -0
- package/dist/client/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/client/grapesjs/img.d.ts +6 -0
- package/dist/client/client/grapesjs/index.d.ts +6 -2
- package/dist/client/client/grapesjs/internal-links.d.ts +1 -1
- package/dist/client/client/grapesjs/keymaps.d.ts +123 -0
- package/dist/client/client/grapesjs/new-page-dialog.d.ts +1 -1
- package/dist/client/client/grapesjs/page-panel.d.ts +7 -1
- package/dist/client/client/grapesjs/project-bar.d.ts +20 -0
- package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
- package/dist/client/client/grapesjs/rich-text.d.ts +4 -1
- package/dist/client/client/grapesjs/semantic.d.ts +1 -1
- package/dist/client/client/grapesjs/settings-sections.d.ts +16 -0
- package/dist/client/client/grapesjs/settings.d.ts +3 -1
- package/dist/client/client/grapesjs/storage.d.ts +1 -0
- package/dist/client/client/grapesjs/symbolDialogs.d.ts +1 -1
- package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
- package/dist/client/client/index.d.ts +5 -0
- package/dist/client/client/publication-transformers.d.ts +42 -0
- package/dist/client/client/publish-custom-code-block.d.ts +4 -0
- package/dist/client/client/publish-fonts.d.ts +4 -0
- package/dist/client/client/utils.d.ts +22 -1
- package/dist/client/constants.d.ts +3 -0
- package/dist/client/css/admin.css +1107 -411
- package/dist/client/css/admin.css.map +1 -0
- package/dist/client/grapesjs-filter-styles/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-fonts/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-fonts/src/commands.d.ts +3 -0
- package/dist/client/grapesjs-fonts/src/fonts.d.ts +4 -0
- package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +4 -0
- package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +4 -0
- package/dist/client/index.html +3 -2
- package/dist/client/js/main.js +305 -170
- package/dist/client/js/main.js.LICENSE.txt +21 -1
- package/dist/client/page.d.ts +1 -1
- package/dist/client/silex-lib/src/ts/client/api.d.ts +90 -0
- package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +54 -0
- package/dist/client/silex-lib/src/ts/client/config.d.ts +102 -0
- package/dist/client/silex-lib/src/ts/client/events.d.ts +23 -0
- package/dist/client/silex-lib/src/ts/client/expose.d.ts +8 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +15 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +73 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +59 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +6 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +6 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +123 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +9 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +21 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +16 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +3 -0
- package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +1 -0
- package/dist/client/silex-lib/src/ts/client/index.d.ts +10 -0
- package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +42 -0
- package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +4 -0
- package/dist/client/silex-lib/src/ts/client/utils.d.ts +22 -0
- package/dist/client/silex-lib/src/ts/constants.d.ts +32 -0
- package/dist/client/silex-lib/src/ts/page.d.ts +2 -0
- package/dist/client/silex-lib/src/ts/types.d.ts +339 -0
- package/dist/client/types.d.ts +59 -35
- package/dist/plugins/client/client/events.d.ts +19 -10
- package/dist/plugins/client/client/events.js +11 -0
- package/dist/plugins/client/client/events.js.map +1 -1
- package/dist/plugins/client/plugins/client/template.js +119 -71
- package/dist/plugins/client/plugins/client/template.js.map +1 -1
- package/dist/plugins/client/plugins/client/website-info.d.ts +1 -0
- package/dist/plugins/client/plugins/client/website-info.js +72 -0
- package/dist/plugins/client/plugins/client/website-info.js.map +1 -0
- package/dist/plugins/client/types.d.ts +59 -35
- package/dist/plugins/client/types.js +23 -0
- package/dist/plugins/client/types.js.map +1 -1
- package/dist/plugins/server/constants.d.ts +3 -0
- package/dist/plugins/server/constants.js +28 -1
- package/dist/plugins/server/constants.js.map +1 -1
- package/dist/plugins/server/plugins/server/Cache.js +9 -5
- package/dist/plugins/server/plugins/server/Cache.js.map +1 -1
- package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.js +127 -0
- package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/FtpConnector.d.ts +5 -2
- package/dist/plugins/server/plugins/server/FtpConnector.js +38 -23
- package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +63 -9
- package/dist/plugins/server/plugins/server/GitlabConnector.js +367 -91
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +16 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +209 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
- package/dist/plugins/server/plugins/server/SslPlugin.js +6 -6
- package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -1
- package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +8 -1
- package/dist/plugins/server/plugins/server/StaticPlugin.js +5 -31
- package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -1
- package/dist/plugins/server/server/config.js +8 -9
- package/dist/plugins/server/server/config.js.map +1 -1
- package/dist/plugins/server/server/connectors/FsHosting.d.ts +3 -2
- package/dist/plugins/server/server/connectors/FsHosting.js +18 -7
- package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -1
- package/dist/plugins/server/server/connectors/FsStorage.d.ts +3 -1
- package/dist/plugins/server/server/connectors/FsStorage.js +35 -28
- package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
- package/dist/plugins/server/server/connectors/connectors.d.ts +7 -7
- package/dist/plugins/server/server/connectors/connectors.js +6 -7
- package/dist/plugins/server/server/connectors/connectors.js.map +1 -1
- package/dist/plugins/server/server/events.js +16 -0
- package/dist/plugins/server/server/events.js.map +1 -1
- package/dist/plugins/server/server/jobs.d.ts +6 -0
- package/dist/plugins/server/server/jobs.js +12 -8
- package/dist/plugins/server/server/jobs.js.map +1 -1
- package/dist/plugins/server/server/utils/validation.js +1 -2
- package/dist/plugins/server/server/utils/validation.js.map +1 -1
- package/dist/plugins/server/types.d.ts +59 -35
- package/dist/plugins/server/types.js +24 -1
- package/dist/plugins/server/types.js.map +1 -1
- package/dist/server/constants.js +27 -1
- package/dist/server/plugins/server/Cache.js +9 -5
- package/dist/server/server/api/api.js +1 -1
- package/dist/server/server/api/connectorApi.js +154 -25
- package/dist/server/server/api/publicationApi.js +17 -25
- package/dist/server/server/api/websiteApi.js +30 -8
- package/dist/server/server/cli.js +9 -6
- package/dist/server/server/config.js +8 -9
- package/dist/server/server/connectors/FsHosting.js +17 -7
- package/dist/server/server/connectors/FsStorage.js +34 -28
- package/dist/server/server/connectors/connectors.js +6 -7
- package/dist/server/server/express.js +4 -5
- package/dist/server/server/index.js +5 -1
- package/dist/server/server/jobs.js +11 -8
- package/dist/server/server/utils/validation.js +1 -2
- package/dist/server/types.js +13 -1
- package/package.json +56 -42
- package/public/assets/download.png +0 -0
- package/public/assets/favicon.ico +0 -0
- package/public/assets/favicon.png +0 -0
- package/public/assets/ftp.png +0 -0
- package/public/assets/gitlab.png +0 -0
- package/public/assets/gitlab2.png +0 -0
- package/public/assets/laptop.png +0 -0
- package/public/assets/logo-silex-small.png +0 -0
- package/public/silex.js +5 -23
- package/src/css/_variables.scss +23 -20
- package/src/css/dialog.scss +27 -1
- package/src/css/fonts.scss +31 -0
- package/src/css/footer.scss +12 -0
- package/src/css/grapesjs-light.scss +180 -0
- package/src/css/grapesjs-plugins.scss +227 -0
- package/src/css/grapesjs.scss +84 -10
- package/src/css/index.scss +12 -1
- package/src/css/light.scss +7 -0
- package/src/css/project-bar.scss +32 -16
- package/src/css/silex.scss +79 -5
- package/src/css/website-info.scss +14 -0
- package/src/html/index.pug +3 -3
- package/src/ts/client/api.ts +38 -14
- package/src/ts/client/assetUrl.test.ts +164 -0
- package/src/ts/client/assetUrl.ts +254 -0
- package/src/ts/client/config.ts +133 -5
- package/src/ts/client/events.ts +21 -9
- package/src/ts/client/grapesjs/LoginDialog.ts +4 -2
- package/src/ts/client/grapesjs/PublicationManager.ts +180 -54
- package/src/ts/client/grapesjs/PublicationUi.ts +122 -86
- package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
- package/src/ts/client/grapesjs/css-props.ts +910 -0
- package/src/ts/client/grapesjs/eleventy.ts +11 -8
- package/{dist/plugins/client/page.js → src/ts/client/grapesjs/footer.ts} +24 -13
- package/src/ts/client/grapesjs/img.ts +147 -0
- package/src/ts/client/grapesjs/index.ts +234 -52
- package/src/ts/client/grapesjs/internal-links.test.ts +2 -1
- package/src/ts/client/grapesjs/internal-links.ts +14 -7
- package/src/ts/client/grapesjs/keymaps.ts +207 -0
- package/src/ts/client/grapesjs/new-page-dialog.ts +2 -3
- package/src/ts/client/grapesjs/page-panel.ts +92 -7
- package/src/ts/client/grapesjs/project-bar.ts +116 -72
- package/src/ts/client/grapesjs/resize-panel.ts +20 -0
- package/src/ts/client/grapesjs/rich-text.ts +21 -13
- package/src/ts/client/grapesjs/semantic.ts +3 -3
- package/src/ts/client/grapesjs/settings-sections.ts +117 -0
- package/src/ts/client/grapesjs/settings.ts +174 -96
- package/src/ts/client/grapesjs/storage.ts +98 -33
- package/src/ts/client/grapesjs/symbolDialogs.ts +3 -4
- package/src/ts/{plugins/server/AuthPlugin.ts → client/grapesjs/view-buttons.ts} +23 -19
- package/src/ts/client/index.ts +35 -16
- package/src/ts/client/publication-transformers.test.ts +207 -0
- package/src/ts/client/publication-transformers.ts +280 -0
- package/{dist/plugins/client/client/utils.js → src/ts/client/publish-custom-code-block.ts} +14 -8
- package/src/ts/client/publish-fonts.ts +36 -0
- package/src/ts/client/utils.ts +53 -1
- package/src/ts/constants.ts +31 -0
- package/src/ts/page.ts +2 -2
- package/src/ts/plugins/client/template.ts +125 -75
- package/src/ts/plugins/client/website-info.ts +84 -0
- package/src/ts/plugins/server/Cache.ts +6 -2
- package/src/ts/plugins/server/DownloadConnector.ts +146 -0
- package/src/ts/plugins/server/FtpConnector.ts +47 -21
- package/src/ts/plugins/server/GitlabConnector.ts +423 -108
- package/src/ts/plugins/server/GitlabHostingConnector.ts +215 -0
- package/src/ts/plugins/server/SslPlugin.ts +5 -5
- package/src/ts/plugins/server/StaticPlugin.ts +3 -30
- package/src/ts/server/api/connectorApi.ts +165 -28
- package/src/ts/server/api/publicationApi.ts +17 -24
- package/src/ts/server/api/websiteApi.ts +57 -14
- package/src/ts/server/cli.ts +7 -5
- package/src/ts/server/config.ts +9 -10
- package/src/ts/server/connectors/FsHosting.ts +14 -8
- package/src/ts/server/connectors/FsStorage.ts +40 -7
- package/src/ts/server/connectors/connectors.ts +8 -6
- package/src/ts/server/events.ts +17 -0
- package/src/ts/server/express.ts +2 -2
- package/src/ts/server/index.ts +6 -0
- package/src/ts/server/jobs.ts +14 -2
- package/src/ts/server/utils/BackwardCompat.ts +530 -529
- package/src/ts/types.ts +78 -34
- package/dist/plugins/client/client/utils.d.ts +0 -1
- package/dist/plugins/client/client/utils.js.map +0 -1
- package/dist/plugins/client/page.d.ts +0 -2
- package/dist/plugins/client/page.js.map +0 -1
- package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +0 -2
- package/dist/plugins/client/plugins/client/publicationRenderer.js +0 -128
- package/dist/plugins/client/plugins/client/publicationRenderer.js.map +0 -1
- package/dist/plugins/server/plugins/server/AuthPlugin.d.ts +0 -1
- package/dist/plugins/server/plugins/server/AuthPlugin.js +0 -38
- package/dist/plugins/server/plugins/server/AuthPlugin.js.map +0 -1
- package/dist/plugins/server/plugins/server/HooksPlugin.d.ts +0 -7
- package/dist/plugins/server/plugins/server/HooksPlugin.js +0 -74
- package/dist/plugins/server/plugins/server/HooksPlugin.js.map +0 -1
- package/src/example-client-config.js +0 -64
- package/src/ts/plugins/client/publicationRenderer.ts +0 -128
- package/src/ts/plugins/server/HooksPlugin.ts +0 -76
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { API_CONNECTOR_LOGIN_CALLBACK, API_CONNECTOR_PATH, API_PATH, WEBSITE_DATA_FILE
|
|
18
|
+
import { API_CONNECTOR_LOGIN_CALLBACK, API_CONNECTOR_PATH, API_PATH, WEBSITE_DATA_FILE } from '../../constants'
|
|
19
19
|
import { ServerConfig } from '../../server/config'
|
|
20
20
|
import { ConnectorFile, ConnectorFileContent, StatusCallback, StorageConnector, contentToBuffer, contentToString, toConnectorData } from '../../server/connectors/connectors'
|
|
21
|
-
import { ApiError, ConnectorType, ConnectorUser, WebsiteData, WebsiteId, WebsiteMeta, WebsiteMetaFileContent } from '../../types'
|
|
21
|
+
import { ApiError, ConnectorType, ConnectorUser, WebsiteData, WebsiteId, WebsiteMeta, WebsiteMetaFileContent, JobStatus } from '../../types'
|
|
22
22
|
import fetch from 'node-fetch'
|
|
23
23
|
import crypto, { createHash } from 'crypto'
|
|
24
|
-
import {
|
|
24
|
+
import { join } from 'path'
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Gitlab connector
|
|
@@ -34,12 +34,18 @@ export interface GitlabOptions {
|
|
|
34
34
|
clientSecret: string
|
|
35
35
|
branch: string
|
|
36
36
|
assetsFolder: string
|
|
37
|
+
//metaRepo: string
|
|
38
|
+
//metaRepoFile: string
|
|
39
|
+
repoPrefix: string
|
|
40
|
+
scope: string
|
|
41
|
+
domain: string
|
|
42
|
+
timeOut: number
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
interface GitlabToken {
|
|
40
|
-
state
|
|
41
|
-
codeVerifier
|
|
42
|
-
codeChallenge
|
|
45
|
+
export interface GitlabToken {
|
|
46
|
+
state?: string
|
|
47
|
+
codeVerifier?: string
|
|
48
|
+
codeChallenge?: string
|
|
43
49
|
token?: {
|
|
44
50
|
access_token: string
|
|
45
51
|
token_type: string
|
|
@@ -50,22 +56,22 @@ interface GitlabToken {
|
|
|
50
56
|
scope: string
|
|
51
57
|
}
|
|
52
58
|
userId?: number
|
|
59
|
+
username?: string
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
gitlab?: GitlabToken
|
|
57
|
-
}
|
|
62
|
+
export type GitlabSession = Record<string, GitlabToken>
|
|
58
63
|
|
|
59
64
|
interface GitlabAction {
|
|
60
|
-
action: 'create' | 'delete' | 'move' | 'update'
|
|
61
|
-
file_path
|
|
65
|
+
action: 'create' | 'delete' | 'move' | 'update' | 'cherry-pick'
|
|
66
|
+
file_path?: string
|
|
62
67
|
content?: string
|
|
68
|
+
commit_id?: string
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
interface GitlabWriteFile {
|
|
66
72
|
branch: string
|
|
67
73
|
commit_message: string
|
|
68
|
-
id
|
|
74
|
+
id?: string
|
|
69
75
|
actions?: GitlabAction[]
|
|
70
76
|
content?: string
|
|
71
77
|
file_path?: string
|
|
@@ -90,98 +96,242 @@ interface GitlabCreateBranch {
|
|
|
90
96
|
ref: string
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
interface GitlabGetTags {
|
|
100
|
+
per_page?: number
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface GitlabCreateTag {
|
|
104
|
+
tag_name: string
|
|
105
|
+
ref: string
|
|
106
|
+
message: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface GitlabFetchCommits {
|
|
110
|
+
ref_name: string
|
|
111
|
+
since: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface MetaRepoFileContent {
|
|
115
|
+
websites: {
|
|
116
|
+
[websiteId: string]: {
|
|
117
|
+
meta: WebsiteMetaFileContent,
|
|
118
|
+
createdAt: string,
|
|
119
|
+
updatedAt: string,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
95
122
|
}
|
|
96
123
|
|
|
124
|
+
const svg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
125
|
+
<svg
|
|
126
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
127
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
128
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
129
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
130
|
+
width="1000"
|
|
131
|
+
height="963.197"
|
|
132
|
+
viewBox="0 0 1000 963.197"
|
|
133
|
+
version="1.1"
|
|
134
|
+
id="svg85">
|
|
135
|
+
<sodipodi:namedview
|
|
136
|
+
id="namedview87"
|
|
137
|
+
pagecolor="#ffffff"
|
|
138
|
+
bordercolor="#666666"
|
|
139
|
+
borderopacity="1.0"
|
|
140
|
+
inkscape:pageshadow="2"
|
|
141
|
+
inkscape:pageopacity="0.0"
|
|
142
|
+
inkscape:pagecheckerboard="0"
|
|
143
|
+
showgrid="false"
|
|
144
|
+
inkscape:zoom="1"
|
|
145
|
+
inkscape:cx="991.5"
|
|
146
|
+
inkscape:cy="964.5"
|
|
147
|
+
inkscape:window-width="1126"
|
|
148
|
+
inkscape:window-height="895"
|
|
149
|
+
inkscape:window-x="774"
|
|
150
|
+
inkscape:window-y="12"
|
|
151
|
+
inkscape:window-maximized="0"
|
|
152
|
+
inkscape:current-layer="svg85" />
|
|
153
|
+
<defs
|
|
154
|
+
id="defs74">
|
|
155
|
+
<style
|
|
156
|
+
id="style72">.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style>
|
|
157
|
+
</defs>
|
|
158
|
+
<g
|
|
159
|
+
id="LOGO"
|
|
160
|
+
transform="matrix(5.2068817,0,0,5.2068817,-489.30756,-507.76085)">
|
|
161
|
+
<path
|
|
162
|
+
class="cls-1"
|
|
163
|
+
d="m 282.83,170.73 -0.27,-0.69 -26.14,-68.22 a 6.81,6.81 0 0 0 -2.69,-3.24 7,7 0 0 0 -8,0.43 7,7 0 0 0 -2.32,3.52 l -17.65,54 h -71.47 l -17.65,-54 a 6.86,6.86 0 0 0 -2.32,-3.53 7,7 0 0 0 -8,-0.43 6.87,6.87 0 0 0 -2.69,3.24 L 97.44,170 l -0.26,0.69 a 48.54,48.54 0 0 0 16.1,56.1 l 0.09,0.07 0.24,0.17 39.82,29.82 19.7,14.91 12,9.06 a 8.07,8.07 0 0 0 9.76,0 l 12,-9.06 19.7,-14.91 40.06,-30 0.1,-0.08 a 48.56,48.56 0 0 0 16.08,-56.04 z"
|
|
164
|
+
id="path76" />
|
|
165
|
+
<path
|
|
166
|
+
class="cls-2"
|
|
167
|
+
d="m 282.83,170.73 -0.27,-0.69 a 88.3,88.3 0 0 0 -35.15,15.8 L 190,229.25 c 19.55,14.79 36.57,27.64 36.57,27.64 l 40.06,-30 0.1,-0.08 a 48.56,48.56 0 0 0 16.1,-56.08 z"
|
|
168
|
+
id="path78" />
|
|
169
|
+
<path
|
|
170
|
+
class="cls-3"
|
|
171
|
+
d="m 153.43,256.89 19.7,14.91 12,9.06 a 8.07,8.07 0 0 0 9.76,0 l 12,-9.06 19.7,-14.91 c 0,0 -17.04,-12.89 -36.59,-27.64 -19.55,14.75 -36.57,27.64 -36.57,27.64 z"
|
|
172
|
+
id="path80" />
|
|
173
|
+
<path
|
|
174
|
+
class="cls-2"
|
|
175
|
+
d="M 132.58,185.84 A 88.19,88.19 0 0 0 97.44,170 l -0.26,0.69 a 48.54,48.54 0 0 0 16.1,56.1 l 0.09,0.07 0.24,0.17 39.82,29.82 c 0,0 17,-12.85 36.57,-27.64 z"
|
|
176
|
+
id="path82" />
|
|
177
|
+
</g>
|
|
178
|
+
</svg>`
|
|
179
|
+
const encodedSvg = encodeURIComponent(svg)
|
|
180
|
+
const ICON = '/assets/gitlab.png'
|
|
181
|
+
|
|
97
182
|
export default class GitlabConnector implements StorageConnector {
|
|
98
183
|
connectorId = 'gitlab'
|
|
99
184
|
connectorType = ConnectorType.STORAGE
|
|
100
185
|
displayName = 'GitLab'
|
|
101
|
-
icon =
|
|
186
|
+
icon = ICON
|
|
102
187
|
disableLogout = false
|
|
103
|
-
color = '#
|
|
104
|
-
background = '
|
|
188
|
+
color = '#2B1B63'
|
|
189
|
+
background = 'rgba(252, 109, 38, 0.2)'
|
|
105
190
|
options: GitlabOptions
|
|
106
191
|
|
|
107
192
|
constructor(private config: ServerConfig, opts: Partial<GitlabOptions>) {
|
|
108
193
|
this.options = {
|
|
109
194
|
branch: 'main',
|
|
110
|
-
assetsFolder: '',
|
|
195
|
+
assetsFolder: 'assets',
|
|
196
|
+
//metaRepo: 'silex-meta',
|
|
197
|
+
//metaRepoFile: 'websites.json',
|
|
198
|
+
repoPrefix: 'silex_',
|
|
199
|
+
scope: 'api', // 'api+read_api+read_user+read_repository+write_repository+email+sudo+profile+openid'
|
|
111
200
|
...opts,
|
|
112
201
|
} as GitlabOptions
|
|
113
202
|
if(!this.options.clientId) throw new Error('Missing Gitlab client ID')
|
|
114
203
|
if(!this.options.clientSecret) throw new Error('Missing Gitlab client secret')
|
|
204
|
+
if(!this.options.domain) throw new Error('Missing Gitlab domain')
|
|
205
|
+
if(!this.options.timeOut) this.options.timeOut = 15000 /* default value */
|
|
115
206
|
}
|
|
116
207
|
|
|
117
208
|
// **
|
|
118
209
|
// Convenience methods for the Gitlab API
|
|
119
210
|
private getAssetPath(path: string): string {
|
|
120
|
-
return
|
|
211
|
+
return encodeURIComponent(join(this.options.assetsFolder, path))
|
|
121
212
|
}
|
|
122
213
|
|
|
123
|
-
|
|
214
|
+
isUsingOfficialInstance(): boolean {
|
|
215
|
+
const gitlabDomainRegexp = /(^|\b)(gitlab\.com)($|\b)/
|
|
216
|
+
return gitlabDomainRegexp.test(this.options.domain)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async createFile(session: GitlabSession, websiteId: WebsiteId, path: string, content: string, isBase64 = false): Promise<void> {
|
|
124
220
|
// Remove leading slash
|
|
125
221
|
const safePath = path.replace(/^\//, '')
|
|
222
|
+
const encodePath = decodeURIComponent(path)
|
|
126
223
|
return this.callApi(session, `api/v4/projects/${websiteId}/repository/files/${safePath}`, 'POST', {
|
|
127
224
|
id: websiteId,
|
|
128
225
|
branch: this.options.branch,
|
|
129
226
|
content,
|
|
130
|
-
commit_message: `Create file ${
|
|
227
|
+
commit_message: `Create file ${encodePath} from Silex`,
|
|
131
228
|
encoding: isBase64 ? 'base64' : undefined,
|
|
132
229
|
})
|
|
133
230
|
}
|
|
134
231
|
|
|
135
|
-
|
|
232
|
+
async updateFile(session: GitlabSession, websiteId: WebsiteId, path: string, content: string, isBase64 = false): Promise<void> {
|
|
136
233
|
// Remove leading slash
|
|
137
234
|
const safePath = path.replace(/^\//, '')
|
|
235
|
+
const encodePath = decodeURIComponent(path)
|
|
138
236
|
return this.callApi(session, `api/v4/projects/${websiteId}/repository/files/${safePath}`, 'PUT', {
|
|
139
237
|
id: websiteId,
|
|
140
238
|
branch: this.options.branch,
|
|
141
239
|
content: await contentToString(content),
|
|
142
|
-
commit_message: `Update website asset ${
|
|
240
|
+
commit_message: `Update website asset ${encodePath} from Silex`,
|
|
143
241
|
encoding: isBase64 ? 'base64' : undefined,
|
|
144
242
|
})
|
|
145
243
|
}
|
|
146
244
|
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const url = `https://gitlab.com/${path}?${tokenParam}${paramsStr}`
|
|
245
|
+
async readFile(session: GitlabSession, websiteId: string, fileName: string): Promise<Buffer> {
|
|
246
|
+
const safePath = fileName.replace(/^\//, '')
|
|
247
|
+
// Call the API
|
|
248
|
+
const url = `${this.options.domain}/api/v4/projects/${websiteId}/repository/files/${safePath}?ref=${this.options.branch}&access_token=${this.getSessionToken(session).token?.access_token}`
|
|
152
249
|
const response = await fetch(url, {
|
|
153
|
-
method,
|
|
250
|
+
method: 'GET',
|
|
154
251
|
headers: {
|
|
155
252
|
'Content-Type': 'application/json',
|
|
156
253
|
},
|
|
157
|
-
body: body ? JSON.stringify(body) : undefined
|
|
158
254
|
})
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
255
|
+
const json = await response.json()
|
|
256
|
+
if(!response.ok) throw new ApiError(`Error reading file "${fileName}" from Gitlab: ${json?.message ?? json?.error ?? response.statusText}`, response.status)
|
|
257
|
+
// From base64 string to buffer
|
|
258
|
+
const buf = Buffer.from(json.content, 'base64')
|
|
259
|
+
// Return the image bytes
|
|
260
|
+
return buf
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/*
|
|
264
|
+
* Get the meta repo path for the current user
|
|
265
|
+
* The meta repo contains a JSON file which contains the list of websites
|
|
266
|
+
*/
|
|
267
|
+
//private getMetaRepoPath(session: GitlabSession): string {
|
|
268
|
+
// if(!this.getSessionToken(session).username) throw new ApiError('Missing Gitlab user ID. User not logged in?', 401)
|
|
269
|
+
// return encodeURIComponent(`${this.getSessionToken(session).username}/${this.options.metaRepo}`)
|
|
270
|
+
//}
|
|
271
|
+
|
|
272
|
+
///**
|
|
273
|
+
// * Initialize the storage with a meta repo
|
|
274
|
+
// */
|
|
275
|
+
//private async initStorage(session: GitlabSession): Promise<void> {
|
|
276
|
+
// // Create the meta repo
|
|
277
|
+
// try {
|
|
278
|
+
// const project = await this.callApi(session, 'api/v4/projects/', 'POST', {
|
|
279
|
+
// name: this.options.metaRepo,
|
|
280
|
+
// }) as any
|
|
281
|
+
// return this.createFile(session, this.getMetaRepoPath(session), this.options.metaRepoFile, JSON.stringify({
|
|
282
|
+
// websites: {}
|
|
283
|
+
// } as MetaRepoFileContent))
|
|
284
|
+
// } catch (e) {
|
|
285
|
+
// console.error('Could not init storage', e.statusCode, e.httpStatusCode, e)
|
|
286
|
+
// throw e
|
|
287
|
+
// }
|
|
288
|
+
//}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Call the Gitlab API with the user's token and handle errors
|
|
292
|
+
*/
|
|
293
|
+
async callApi(session: GitlabSession, path: string, method: 'POST' | 'GET' | 'PUT' | 'DELETE' = 'GET', body: GitlabWriteFile | GitlabGetToken | GitlabWebsiteName | GitlabCreateBranch | GitlabGetTags | GitlabCreateTag | GitlabFetchCommits | null = null, params: any = {}): Promise<any> {
|
|
294
|
+
const token = this.getSessionToken(session).token
|
|
295
|
+
const tokenParam = token ? `access_token=${token.access_token}&` : ''
|
|
296
|
+
const paramsStr = Object.entries(params).map(([k, v]) => `${k}=${encodeURIComponent((v as any).toString())}`).join('&')
|
|
297
|
+
const url = `${this.options.domain}/${path}?${tokenParam}${paramsStr}`
|
|
298
|
+
const headers = {
|
|
299
|
+
'Content-Type': 'application/json',
|
|
300
|
+
}
|
|
301
|
+
if (method === 'GET' && body) {
|
|
302
|
+
console.error('Gitlab API error (4) - GET request with body', {url, method, body, params})
|
|
303
|
+
}
|
|
304
|
+
// With or without body
|
|
305
|
+
let response
|
|
162
306
|
try {
|
|
163
|
-
|
|
307
|
+
response = await fetch(url, body && method !== 'GET' ? {
|
|
308
|
+
method,
|
|
309
|
+
headers,
|
|
310
|
+
body: body ? JSON.stringify(body) : undefined
|
|
311
|
+
} : {
|
|
312
|
+
method,
|
|
313
|
+
headers,
|
|
314
|
+
})
|
|
164
315
|
} catch (e) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
throw e
|
|
168
|
-
} else {
|
|
169
|
-
// Useless error linked to the fact that the response is not JSON
|
|
170
|
-
//console.error('Gitlab API error - could not parse response', response.status, response.statusText, {url, method, body, params, text})
|
|
171
|
-
return text
|
|
172
|
-
}
|
|
316
|
+
console.error('Gitlab API error (0)', e)
|
|
317
|
+
throw new ApiError(`Gitlab API error (0): ${e.message} ${e.code} ${e.name} ${e.type}`, 500)
|
|
173
318
|
}
|
|
174
|
-
|
|
175
|
-
|
|
319
|
+
let json: { message: string, error: string } | any
|
|
320
|
+
// Handle the case when the server returns a non-JSON response (e.g. 400 Bad Request)
|
|
321
|
+
const text = await response.text()
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
if (text.includes('A file with this name doesn\'t exist')) {
|
|
324
|
+
throw new ApiError('Gitlab API error (5): Not Found', 404)
|
|
325
|
+
} else if (response.status === 401 && this.getSessionToken(session).token?.refresh_token) {
|
|
176
326
|
// Refresh the token
|
|
177
|
-
const token = session
|
|
327
|
+
const token = this.getSessionToken(session).token
|
|
178
328
|
const body = {
|
|
179
329
|
grant_type: 'refresh_token',
|
|
180
|
-
refresh_token: token
|
|
330
|
+
refresh_token: token?.refresh_token,
|
|
181
331
|
client_id: this.options.clientId,
|
|
182
332
|
client_secret: this.options.clientSecret,
|
|
183
333
|
}
|
|
184
|
-
const response = await fetch('
|
|
334
|
+
const response = await fetch(this.options.domain + '/oauth/token', {
|
|
185
335
|
method: 'POST',
|
|
186
336
|
headers: {
|
|
187
337
|
'Content-Type': 'application/json',
|
|
@@ -190,18 +340,38 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
190
340
|
})
|
|
191
341
|
const refreshJson = await response.json()
|
|
192
342
|
if (response.ok) {
|
|
193
|
-
session
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
343
|
+
this.setSessionToken(session, {
|
|
344
|
+
token: {
|
|
345
|
+
...token,
|
|
346
|
+
...refreshJson,
|
|
347
|
+
},
|
|
348
|
+
} as GitlabToken)
|
|
197
349
|
return await this.callApi(session, path, method, body as any, params)
|
|
198
350
|
} else {
|
|
199
|
-
|
|
200
|
-
|
|
351
|
+
const message = typeof refreshJson?.message === 'object' ? Object.entries(refreshJson.message).map(entry => entry.join(' ')).join(' ') : refreshJson?.message ?? refreshJson?.error ?? response.statusText
|
|
352
|
+
console.error('Gitlab API error (2) - could not refresh token', response.status, response.statusText, {message}, 'refresh_token:', token?.refresh_token)
|
|
353
|
+
// Workaround for when the token is invalid
|
|
354
|
+
// It happens often which is not normal (refresh token should last 6 months)
|
|
355
|
+
this.logout(session)
|
|
356
|
+
// Notify the user
|
|
357
|
+
throw new ApiError(`Gitlab API error (2): ${message}`, response.status)
|
|
201
358
|
}
|
|
202
359
|
} else {
|
|
203
|
-
|
|
204
|
-
|
|
360
|
+
const message = typeof json?.message === 'object' ? Object.entries(json.message).map(entry => entry.join(' ')).join(' ') : json?.message ?? json?.error ?? response.statusText
|
|
361
|
+
console.error('Gitlab API error (1)', response.status, response.statusText, {url, method, body, params, text, message})
|
|
362
|
+
throw new ApiError(`Gitlab API error (1): ${message}`, response.status)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
json = JSON.parse(text)
|
|
367
|
+
} catch (e) {
|
|
368
|
+
if(!response.ok) {
|
|
369
|
+
// A real error
|
|
370
|
+
throw e
|
|
371
|
+
} else {
|
|
372
|
+
// Useless error linked to the fact that the response is not JSON
|
|
373
|
+
console.error('Gitlab API error (3) - could not parse response', response.status, response.statusText, {url, method, body, params, text})
|
|
374
|
+
return text
|
|
205
375
|
}
|
|
206
376
|
}
|
|
207
377
|
return json
|
|
@@ -245,14 +415,23 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
245
415
|
const codeChallenge = await this.generateCodeChallenge(codeVerifier)
|
|
246
416
|
|
|
247
417
|
// Store the code verifier and code challenge in the session
|
|
248
|
-
session
|
|
249
|
-
...session
|
|
418
|
+
this.setSessionToken(session, {
|
|
419
|
+
...this.getSessionToken(session),
|
|
250
420
|
state,
|
|
251
421
|
codeVerifier,
|
|
252
422
|
codeChallenge,
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
423
|
+
})
|
|
424
|
+
return `${this.options.domain}/oauth/authorize?client_id=${this.options.clientId}&redirect_uri=${redirect_uri}&response_type=code&state=${this.getSessionToken(session).state}&scope=${this.options.scope}&code_challenge=${codeChallenge}&code_challenge_method=S256`
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
getSessionToken(session: GitlabSession | undefined): GitlabToken {
|
|
428
|
+
return (session ?? {})[this.connectorId] ?? {}
|
|
429
|
+
}
|
|
430
|
+
setSessionToken(session: GitlabSession, token: GitlabToken): void {
|
|
431
|
+
session[this.connectorId] = token
|
|
432
|
+
}
|
|
433
|
+
resetSessionToken(session: GitlabSession): void {
|
|
434
|
+
delete session[this.connectorId]
|
|
256
435
|
}
|
|
257
436
|
|
|
258
437
|
getOptions(formData: object): object {
|
|
@@ -268,7 +447,7 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
268
447
|
}
|
|
269
448
|
|
|
270
449
|
async isLoggedIn(session: GitlabSession): Promise<boolean> {
|
|
271
|
-
return !!session
|
|
450
|
+
return !!this.getSessionToken(session).token
|
|
272
451
|
}
|
|
273
452
|
|
|
274
453
|
/**
|
|
@@ -277,11 +456,21 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
277
456
|
* OAuth2 Step #2 from https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-with-proof-key-for-code-exchange-pkce
|
|
278
457
|
*/
|
|
279
458
|
async setToken(session: GitlabSession, loginResult: any): Promise<void> {
|
|
280
|
-
|
|
281
|
-
if(!
|
|
282
|
-
|
|
459
|
+
const sessionToken = this.getSessionToken(session)
|
|
460
|
+
if(!loginResult.state || loginResult.state !== sessionToken?.state) {
|
|
461
|
+
this.logout(session)
|
|
462
|
+
throw new ApiError('Invalid state', 401)
|
|
463
|
+
}
|
|
464
|
+
if(!sessionToken?.codeVerifier) {
|
|
465
|
+
this.logout(session)
|
|
466
|
+
throw new ApiError('Missing code verifier', 401)
|
|
467
|
+
}
|
|
468
|
+
if(!sessionToken?.codeChallenge) {
|
|
469
|
+
this.logout(session)
|
|
470
|
+
throw new ApiError('Missing code challenge', 401)
|
|
471
|
+
}
|
|
283
472
|
|
|
284
|
-
const
|
|
473
|
+
const response = await fetch(this.options.domain + '/oauth/token', {
|
|
285
474
|
method: 'POST',
|
|
286
475
|
headers: {
|
|
287
476
|
'Content-Type': 'application/json',
|
|
@@ -292,20 +481,31 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
292
481
|
code: loginResult.code,
|
|
293
482
|
grant_type: 'authorization_code',
|
|
294
483
|
redirect_uri: this.getRedirect(),
|
|
295
|
-
code_verifier:
|
|
484
|
+
code_verifier: sessionToken.codeVerifier,
|
|
296
485
|
}),
|
|
297
486
|
})
|
|
298
|
-
.then((response) => response.json())
|
|
299
487
|
|
|
300
|
-
|
|
488
|
+
const token = await response.json()
|
|
489
|
+
|
|
490
|
+
// Store the token in the session
|
|
491
|
+
this.setSessionToken(session, {
|
|
492
|
+
token,
|
|
493
|
+
...this.getSessionToken(session),
|
|
494
|
+
})
|
|
301
495
|
|
|
302
496
|
// We need to get the user ID for listWebsites
|
|
303
497
|
const user = await this.callApi(session, 'api/v4/user') as any
|
|
304
|
-
|
|
498
|
+
|
|
499
|
+
// Store the user details in the session
|
|
500
|
+
this.setSessionToken(session, {
|
|
501
|
+
...this.getSessionToken(session),
|
|
502
|
+
userId: user.id,
|
|
503
|
+
username: user.username,
|
|
504
|
+
})
|
|
305
505
|
}
|
|
306
506
|
|
|
307
507
|
async logout(session: GitlabSession): Promise<void> {
|
|
308
|
-
|
|
508
|
+
this.resetSessionToken(session)
|
|
309
509
|
}
|
|
310
510
|
|
|
311
511
|
async getUser(session: GitlabSession): Promise<ConnectorUser> {
|
|
@@ -313,19 +513,49 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
313
513
|
return {
|
|
314
514
|
name: user.name,
|
|
315
515
|
email: user.email,
|
|
516
|
+
picture: user.avatar_url,
|
|
316
517
|
storage: await toConnectorData(session, this as StorageConnector),
|
|
317
518
|
}
|
|
318
519
|
}
|
|
319
520
|
|
|
320
521
|
async listWebsites(session: GitlabSession): Promise<WebsiteMeta[]> {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
})
|
|
522
|
+
//try {
|
|
523
|
+
// const result = await this.callApi(session, `api/v4/projects/${this.getMetaRepoPath(session)}/repository/files/${this.options.metaRepoFile}`, 'GET', null, {
|
|
524
|
+
// ref: this.options.branch,
|
|
525
|
+
// })
|
|
526
|
+
// const { content } = result
|
|
527
|
+
// const contentDecoded = Buffer.from(content, 'base64').toString('utf8')
|
|
528
|
+
// const websites = (JSON.parse(contentDecoded) as MetaRepoFileContent).websites
|
|
529
|
+
// return Object.entries(websites).map(([websiteId, {meta, createdAt, updatedAt}]) => ({
|
|
530
|
+
// websiteId,
|
|
531
|
+
// createdAt: new Date(createdAt),
|
|
532
|
+
// updatedAt: new Date(updatedAt),
|
|
533
|
+
// ...meta,
|
|
534
|
+
// }))
|
|
535
|
+
//} catch (e) {
|
|
536
|
+
// console.error('Could not list websites', e.statusCode, e.httpStatusCode, e.code)
|
|
537
|
+
// if (e.statusCode === 404 || e.httpStatusCode === 404) {
|
|
538
|
+
// await this.initStorage(session)
|
|
539
|
+
// return []
|
|
540
|
+
// } else {
|
|
541
|
+
// throw e
|
|
542
|
+
// }
|
|
543
|
+
//}
|
|
544
|
+
const userId = this.getSessionToken(session).userId
|
|
545
|
+
if(!userId) {
|
|
546
|
+
this.logout(session)
|
|
547
|
+
throw new ApiError('Missing Gitlab user ID. User not logged in?', 401)
|
|
548
|
+
}
|
|
549
|
+
const projects = await this.callApi(session, `api/v4/users/${userId}/projects`) as any[]
|
|
550
|
+
return projects
|
|
551
|
+
.filter(p => p.name.startsWith(this.options.repoPrefix))
|
|
552
|
+
.map(p => ({
|
|
553
|
+
websiteId: p.id,
|
|
554
|
+
name: p.name.replace(this.options.repoPrefix, ''),
|
|
555
|
+
createdAt: p.created_at,
|
|
556
|
+
updatedAt: p.last_activity_at,
|
|
557
|
+
connectorUserSettings: {},
|
|
558
|
+
}))
|
|
329
559
|
}
|
|
330
560
|
|
|
331
561
|
async readWebsite(session: GitlabSession, websiteId: string): Promise<WebsiteData> {
|
|
@@ -340,10 +570,10 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
340
570
|
|
|
341
571
|
async createWebsite(session: GitlabSession, websiteMeta: WebsiteMetaFileContent): Promise<WebsiteId> {
|
|
342
572
|
const project = await this.callApi(session, 'api/v4/projects/', 'POST', {
|
|
343
|
-
name: websiteMeta.name,
|
|
573
|
+
name: this.options.repoPrefix + websiteMeta.name,
|
|
344
574
|
}) as any
|
|
345
575
|
await this.createFile(session, project.id, WEBSITE_DATA_FILE, JSON.stringify({} as WebsiteData))
|
|
346
|
-
await this.createFile(session, project.id, WEBSITE_META_DATA_FILE, JSON.stringify(websiteMeta))
|
|
576
|
+
//await this.createFile(session, project.id, WEBSITE_META_DATA_FILE, JSON.stringify(websiteMeta))
|
|
347
577
|
//await this.updateWebsite(session, project.id, {} as WebsiteData)
|
|
348
578
|
//await this.setWebsiteMeta(session, project.id, websiteMeta)
|
|
349
579
|
return project.id
|
|
@@ -354,20 +584,85 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
354
584
|
branch: this.options.branch,
|
|
355
585
|
commit_message: 'Update website data from Silex',
|
|
356
586
|
content: JSON.stringify(websiteData),
|
|
357
|
-
file_path: WEBSITE_DATA_FILE,
|
|
358
587
|
id: websiteId,
|
|
359
588
|
})
|
|
360
589
|
}
|
|
361
590
|
|
|
362
591
|
async deleteWebsite(session: GitlabSession, websiteId: WebsiteId): Promise<void> {
|
|
592
|
+
// Delete repo
|
|
363
593
|
await this.callApi(session, `api/v4/projects/${websiteId}`, 'DELETE')
|
|
594
|
+
//// Load the meta repo data
|
|
595
|
+
//const file = await this.callApi(session, `api/v4/projects/${this.getMetaRepoPath(session)}/repository/files/${this.options.metaRepoFile}`, 'GET', null, {
|
|
596
|
+
// ref: this.options.branch,
|
|
597
|
+
//})
|
|
598
|
+
//const metaRepo = JSON.parse(Buffer.from(file.content, 'base64').toString('utf8')) as MetaRepoFileContent
|
|
599
|
+
//const data = metaRepo.websites[websiteId]
|
|
600
|
+
//if(!data) throw new ApiError(`Website ${websiteId} not found`, 404)
|
|
601
|
+
//// Update or create the website meta data
|
|
602
|
+
//delete metaRepo.websites[websiteId]
|
|
603
|
+
//// Save the meta repo data
|
|
604
|
+
//const project = await this.callApi(session, `api/v4/projects/${this.getMetaRepoPath(session)}/repository/files/${this.options.metaRepoFile}`, 'PUT', {
|
|
605
|
+
// branch: this.options.branch,
|
|
606
|
+
// commit_message: `Delete meta data of ${data.meta.name} (${websiteId}) from Silex`,
|
|
607
|
+
// content: JSON.stringify(metaRepo),
|
|
608
|
+
// file_path: this.options.metaRepoFile,
|
|
609
|
+
//})
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async duplicateWebsite(session: GitlabSession, websiteId: string): Promise<void> {
|
|
613
|
+
// Get the repo meta data
|
|
614
|
+
const meta = await this.getWebsiteMeta(session, websiteId)
|
|
615
|
+
// List all the repository files
|
|
616
|
+
const blobs = await this.callApi(session, `api/v4/projects/${websiteId}/repository/tree`, 'GET', null, {
|
|
617
|
+
recursive: true,
|
|
618
|
+
})
|
|
619
|
+
const files = blobs
|
|
620
|
+
.filter(item => item.type === 'blob')
|
|
621
|
+
.map(item => item.path)
|
|
622
|
+
// Create a new repo
|
|
623
|
+
const newId = await this.createWebsite(session, {
|
|
624
|
+
...meta,
|
|
625
|
+
name: meta.name + ' Copy ' + new Date().toISOString().replace(/T.*/, '') + ' ' + Math.random().toString(36).substring(2, 4),
|
|
626
|
+
})
|
|
627
|
+
// Upload all files
|
|
628
|
+
for(const file of files) {
|
|
629
|
+
const path = encodeURIComponent(file)
|
|
630
|
+
const content = await this.readFile(session, websiteId, path)
|
|
631
|
+
// From buffer to string
|
|
632
|
+
const contentStr = content.toString('base64')
|
|
633
|
+
switch(file) {
|
|
634
|
+
case WEBSITE_DATA_FILE:
|
|
635
|
+
await this.updateFile(session, newId, path, contentStr, true)
|
|
636
|
+
break
|
|
637
|
+
default:
|
|
638
|
+
await this.createFile(session, newId, path, contentStr, true)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
364
641
|
}
|
|
365
642
|
|
|
366
643
|
async getWebsiteMeta(session: GitlabSession, websiteId: WebsiteId): Promise<WebsiteMeta> {
|
|
367
|
-
const
|
|
644
|
+
//const file = await this.callApi(session, `api/v4/projects/${this.getMetaRepoPath(session)}/repository/files/${this.options.metaRepoFile}`, 'GET', null, {
|
|
645
|
+
// ref: this.options.branch,
|
|
646
|
+
//})
|
|
647
|
+
//const metaRepo = JSON.parse(Buffer.from(file.content, 'base64').toString('utf8')) as MetaRepoFileContent
|
|
648
|
+
//if(!metaRepo.websites[websiteId]) throw new ApiError(`Website ${websiteId} not found`, 404)
|
|
649
|
+
//return {
|
|
650
|
+
// websiteId,
|
|
651
|
+
// createdAt: new Date(metaRepo.websites[websiteId].createdAt),
|
|
652
|
+
// updatedAt: new Date(metaRepo.websites[websiteId].updatedAt),
|
|
653
|
+
// ...metaRepo.websites[websiteId].meta,
|
|
654
|
+
//}
|
|
655
|
+
// const response = await this.callApi(session, `api/v4/projects/${websiteId}/repository/files/${WEBSITE_META_DATA_FILE}`, 'GET', null, {
|
|
656
|
+
// ref: this.options.branch,
|
|
657
|
+
// }) as any
|
|
658
|
+
// Base64 to string to JSON
|
|
659
|
+
// const contentDecoded = Buffer.from(response.content, 'base64').toString('utf8')
|
|
660
|
+
// const websiteMeta = JSON.parse(contentDecoded) as WebsiteMetaFileContent
|
|
661
|
+
const project = await this.callApi(session, `api/v4/projects/${websiteId}`)
|
|
368
662
|
return {
|
|
369
|
-
websiteId
|
|
370
|
-
name: project.name,
|
|
663
|
+
websiteId,
|
|
664
|
+
name: project.name.replace(this.options.repoPrefix, ''),
|
|
665
|
+
imageUrl: project.avatar_url,
|
|
371
666
|
createdAt: project.created_at,
|
|
372
667
|
updatedAt: project.last_activity_at,
|
|
373
668
|
connectorUserSettings: {},
|
|
@@ -375,49 +670,69 @@ export default class GitlabConnector implements StorageConnector {
|
|
|
375
670
|
}
|
|
376
671
|
|
|
377
672
|
async setWebsiteMeta(session: GitlabSession, websiteId: WebsiteId, websiteMeta: WebsiteMetaFileContent): Promise<void> {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
673
|
+
//// Load the meta repo data
|
|
674
|
+
//const file = await this.callApi(session, `api/v4/projects/${this.getMetaRepoPath(session)}/repository/files/${this.options.metaRepoFile}`, 'GET', null, {
|
|
675
|
+
// ref: this.options.branch,
|
|
676
|
+
//})
|
|
677
|
+
//const metaRepo = JSON.parse(Buffer.from(file.content, 'base64').toString('utf8')) as MetaRepoFileContent
|
|
678
|
+
//// Update or create the website meta data
|
|
679
|
+
//metaRepo.websites[websiteId] = {
|
|
680
|
+
// updatedAt: new Date().toISOString(),
|
|
681
|
+
// createdAt: metaRepo.websites[websiteId]?.createdAt ?? new Date().toISOString(),
|
|
682
|
+
// meta: websiteMeta,
|
|
683
|
+
//}
|
|
684
|
+
//// Save the meta repo data
|
|
685
|
+
//const project = await this.callApi(session, `api/v4/projects/${this.getMetaRepoPath(session)}/repository/files/${this.options.metaRepoFile}`, 'PUT', {
|
|
686
|
+
// branch: this.options.branch,
|
|
687
|
+
// commit_message: `Update website meta data of ${websiteMeta.name} (${websiteId}) from Silex`,
|
|
688
|
+
// content: JSON.stringify(metaRepo),
|
|
689
|
+
// file_path: this.options.metaRepoFile,
|
|
690
|
+
//})
|
|
691
|
+
|
|
692
|
+
// Rename the repo if needed
|
|
693
|
+
const oldMeta = await this.getWebsiteMeta(session, websiteId)
|
|
694
|
+
if(websiteMeta.name !== oldMeta.name) {
|
|
695
|
+
await this.callApi(session, `api/v4/projects/${websiteId}`, 'PUT', {
|
|
696
|
+
name: this.options.repoPrefix + websiteMeta.name,
|
|
697
|
+
})
|
|
698
|
+
}
|
|
699
|
+
//// Update the metadata file
|
|
700
|
+
//await this.callApi(session, `api/v4/projects/${websiteId}/repository/files/${WEBSITE_META_DATA_FILE}`, 'PUT', {
|
|
701
|
+
// branch: this.options.branch,
|
|
702
|
+
// commit_message: 'Update website meta data from Silex',
|
|
703
|
+
// content: JSON.stringify(websiteMeta),
|
|
704
|
+
// file_path: WEBSITE_META_DATA_FILE,
|
|
705
|
+
// id: websiteId,
|
|
706
|
+
//})
|
|
385
707
|
}
|
|
386
708
|
|
|
387
709
|
async writeAssets(session: GitlabSession, websiteId: string, files: ConnectorFile[], status?: StatusCallback | undefined): Promise<void> {
|
|
710
|
+
status && await status({ message: 'in progress...', status: JobStatus.IN_PROGRESS })
|
|
388
711
|
// For each file
|
|
389
712
|
for (const file of files) {
|
|
390
713
|
// Convert to base64
|
|
391
714
|
const content = (await contentToBuffer(file.content)).toString('base64')
|
|
715
|
+
const path = this.getAssetPath(file.path)
|
|
716
|
+
|
|
392
717
|
try {
|
|
393
|
-
await this.updateFile(session, websiteId,
|
|
718
|
+
await this.updateFile(session, websiteId, path, content, true)
|
|
394
719
|
} catch (e) {
|
|
395
720
|
// If the file does not exist, create it
|
|
396
|
-
if (e.statusCode === 404 || e.message.endsWith('A file with this name doesn\'t exist')) {
|
|
397
|
-
await this.createFile(session, websiteId,
|
|
721
|
+
if (e.statusCode === 404 || e.httpStatusCode === 404 || e.message.endsWith('A file with this name doesn\'t exist')) {
|
|
722
|
+
await this.createFile(session, websiteId, path, content, true)
|
|
398
723
|
} else {
|
|
724
|
+
status && await status({ message: 'Error', status: JobStatus.ERROR })
|
|
399
725
|
throw e
|
|
400
726
|
}
|
|
401
727
|
}
|
|
402
728
|
}
|
|
729
|
+
status && await status({ message: 'Successfull', status: JobStatus.SUCCESS })
|
|
403
730
|
}
|
|
404
731
|
|
|
405
732
|
async readAsset(session: GitlabSession, websiteId: string, fileName: string): Promise<ConnectorFileContent> {
|
|
406
733
|
// Remove leading slash
|
|
407
734
|
const finalPath = this.getAssetPath(fileName)
|
|
408
|
-
|
|
409
|
-
const url = `https://gitlab.com/api/v4/projects/${websiteId}/repository/files/${finalPath}?ref=${this.options.branch}&access_token=${session.gitlab?.token?.access_token}`
|
|
410
|
-
const response = await fetch(url, {
|
|
411
|
-
method: 'GET',
|
|
412
|
-
headers: {
|
|
413
|
-
'Content-Type': 'application/json',
|
|
414
|
-
},
|
|
415
|
-
})
|
|
416
|
-
const json = await response.json()
|
|
417
|
-
if(!response.ok) throw new ApiError(`Gitlab API error: ${json?.message ?? json?.error ?? response.statusText}`, response.status)
|
|
418
|
-
// From base64 string to buffer
|
|
419
|
-
const buf = Buffer.from(json.content, 'base64')
|
|
420
|
-
return buf
|
|
735
|
+
return this.readFile(session, websiteId, finalPath)
|
|
421
736
|
}
|
|
422
737
|
|
|
423
738
|
async deleteAssets(session: GitlabSession, websiteId: string, fileNames: string[]): Promise<void> {
|