Skip to content

Commit

Permalink
WIP : pour analyse suite modif gitlabConnector par silexlabs#1589
Browse files Browse the repository at this point in the history
  • Loading branch information
Ubuntu committed May 20, 2024
1 parent 549bf7b commit 45c921a
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 7 deletions.
19 changes: 12 additions & 7 deletions src/ts/plugins/server/GitlabConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { API_CONNECTOR_LOGIN_CALLBACK, API_CONNECTOR_PATH, API_PATH, WEBSITE_DATA_FILE } from '../../constants'
import { ServerConfig } from '../../server/config'
import { ConnectorFile, ConnectorFileContent, StatusCallback, StorageConnector, contentToBuffer, contentToString, toConnectorData } from '../../server/connectors/connectors'
import { ApiError, ConnectorType, ConnectorUser, WebsiteData, WebsiteId, WebsiteMeta, WebsiteMetaFileContent } from '../../types'
import { ApiError, ConnectorType, ConnectorUser, WebsiteData, WebsiteId, WebsiteMeta, WebsiteMetaFileContent, JobStatus } from '../../types'
import fetch from 'node-fetch'
import crypto, { createHash } from 'crypto'
import { join } from 'path'
Expand All @@ -41,7 +41,7 @@ export interface GitlabOptions {
domain: string
}

interface GitlabToken {
export interface GitlabToken {
state?: string
codeVerifier?: string
codeChallenge?: string
Expand Down Expand Up @@ -194,26 +194,28 @@ export default class GitlabConnector implements StorageConnector {
return encodeURIComponent(join(this.options.assetsFolder, path))
}

private async createFile(session: GitlabSession, websiteId: WebsiteId, path: string, content: string, isBase64 = false): Promise<void> {
async createFile(session: GitlabSession, websiteId: WebsiteId, path: string, content: string, isBase64 = false): Promise<void> {
// Remove leading slash
const safePath = path.replace(/^\//, '')
const encodePath = decodeURIComponent(path)
return this.callApi(session, `api/v4/projects/${websiteId}/repository/files/${safePath}`, 'POST', {
id: websiteId,
branch: this.options.branch,
content,
commit_message: `Create file ${path} from Silex`,
commit_message: `Create file ${encodePath} from Silex`,
encoding: isBase64 ? 'base64' : undefined,
})
}

private async updateFile(session: GitlabSession, websiteId: WebsiteId, path: string, content: string, isBase64 = false): Promise<void> {
async updateFile(session: GitlabSession, websiteId: WebsiteId, path: string, content: string, isBase64 = false): Promise<void> {
// Remove leading slash
const safePath = path.replace(/^\//, '')
const encodePath = decodeURIComponent(path)
return this.callApi(session, `api/v4/projects/${websiteId}/repository/files/${safePath}`, 'PUT', {
id: websiteId,
branch: this.options.branch,
content: await contentToString(content),
commit_message: `Update website asset ${path} from Silex`,
commit_message: `Update website asset ${encodePath} from Silex`,
encoding: isBase64 ? 'base64' : undefined,
})
}
Expand Down Expand Up @@ -266,7 +268,7 @@ export default class GitlabConnector implements StorageConnector {
/**
* Call the Gitlab API with the user's token and handle errors
*/
private async callApi(session: GitlabSession, path: string, method: 'POST' | 'GET' | 'PUT' | 'DELETE' = 'GET', body: GitlabWriteFile | GitlabGetToken | GitlabWebsiteName | GitlabCreateBranch | null = null, params: any = {}): Promise<any> {
async callApi(session: GitlabSession, path: string, method: 'POST' | 'GET' | 'PUT' | 'DELETE' = 'GET', body: GitlabWriteFile | GitlabGetToken | GitlabWebsiteName | GitlabCreateBranch | null = null, params: any = {}): Promise<any> {
const token = this.getSessionToken(session).token
const tokenParam = token ? `access_token=${token.access_token}&` : ''
const paramsStr = Object.entries(params).map(([k, v]) => `${k}=${encodeURIComponent((v as any).toString())}`).join('&')
Expand Down Expand Up @@ -663,6 +665,7 @@ export default class GitlabConnector implements StorageConnector {
}

async writeAssets(session: GitlabSession, websiteId: string, files: ConnectorFile[], status?: StatusCallback | undefined): Promise<void> {
status && status({ message: 'in progress...', status: JobStatus.IN_PROGRESS })
// For each file
for (const file of files) {
// Convert to base64
Expand All @@ -676,10 +679,12 @@ export default class GitlabConnector implements StorageConnector {
if (e.statusCode === 404 || e.httpStatusCode === 404 || e.message.endsWith('A file with this name doesn\'t exist')) {
await this.createFile(session, websiteId, path, content, true)
} else {
status && status({ message: 'Error', status: JobStatus.ERROR })
throw e
}
}
}
status && status({ message: 'Successfull', status: JobStatus.SUCCESS })
}

async readAsset(session: GitlabSession, websiteId: string, fileName: string): Promise<ConnectorFileContent> {
Expand Down
109 changes: 109 additions & 0 deletions src/ts/plugins/server/GitlabHostingConnector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Silex website builder, free/libre no-code tool for makers.
* Copyright (c) 2023 lexoyo and Silex Labs foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
* Gitlab hosting connector
* @fileoverview Gitlab hosting connector for Silex, connect to the user's Gitlab account to host websites with .gitlab-ci.yml file template for plain html by default
* @see https://docs.gitlab.com/ee/api/oauth2.html
* @see https://docs.gitlab.com/ee/user/project/pages/getting_started/pages_ci_cd_template.html
*/

import GitlabConnector, { GitlabOptions, GitlabToken } from './GitlabConnector'
import { HostingConnector, ConnectorFile } from '../../server/connectors/connectors'
import { ConnectorType, WebsiteId, JobData, JobStatus, PublicationJobData } from '../../types'
import { JobManager } from '../../server/jobs'
import { join } from 'path'
import { ServerConfig } from '../../server/config'

//type GitlabSession = ConnectorSession
//type GitlabOption = GitlabOptions
type GitlabSession = Record<string, GitlabToken>

export default class GitlabHostingConnector extends GitlabConnector implements HostingConnector/*<GitlabSession>*/ {
connectorId = 'gitlab_hosting'
displayName = 'Gitlab hosting'
connectorType = ConnectorType.HOSTING

constructor( config: ServerConfig, opts: Partial<GitlabOptions>) {
super (config, opts)
/* public directory for standard gitlab pages */
this.options.assetsFolder= 'public'
}

async publish(session: GitlabSession, websiteId: WebsiteId, files: ConnectorFile[], {startJob, jobSuccess, jobError}: JobManager): Promise<JobData> {
const job = startJob(`Publishing to ${this.displayName}`) as PublicationJobData
job.logs = [[`Publishing to ${this.displayName}`]]
job.errors = [[]]
/* Configuration file .gitlab-ci.yml contains template for plain html Gitlab pages*/
const pathYml = '.gitlab-ci.yml'
const contentYml =
`# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
# Full project: https://gitlab.com/pages/plain-html
image: busybox
pages:
stage: deploy
environment: production
script:
- echo "The site will be deployed to $CI_PAGES_URL"
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH`
console.log(session)
try {
await this.readFile(session, websiteId, pathYml)
} catch (e) {
// If the file .gitlab-ci.yml does not exist, create it, otherwise do nothing (do not overwriting existing one)
if (e.statusCode === 404 || e.httpStatusCode === 404 || e.message.endsWith('A file with this name doesn\'t exist')) {
await this.createFile(session, websiteId, pathYml, contentYml)
}
}

/* Get Url Gitlab Pages */
const response = await this.callApi(session, `api/v4/projects/${websiteId}/pages`, 'GET')
const publicationUrl = response.url

/* publishing all files for website*/
await this.writeAssets(session, websiteId, files, async ({status, message}) => {
// Update the job status
job.status = status
job.message = message
job.logs[0].push(message)
if(status === JobStatus.SUCCESS) {
jobSuccess(job.jobId, '<a href="' + publicationUrl + '" target="_blank">' + publicationUrl + '<a>')
} else if(status === JobStatus.ERROR) {
job.errors[0].push(message)
jobError(job.jobId, message)
}
})
return job
}

async getUrl(session: GitlabSession, websiteId: WebsiteId): Promise<string> {
const id = websiteId
const filePath = join(this.options.domain, id, 'index.html')
const fileUrl = new URL(filePath, 'file://')
return fileUrl.toString()
}
}

0 comments on commit 45c921a

Please sign in to comment.