Gitlab支持优化

This commit is contained in:
xiaoqi.cxq 2023-02-23 15:33:55 +08:00
parent 4d8ff0ea0c
commit b1691e0d4f
10 changed files with 109 additions and 26 deletions

View File

@ -69,6 +69,7 @@ StackEdit中文版
- 支持图片直接存储到当前文档空间2022-10-29 - 支持图片直接存储到当前文档空间2022-10-29
- 支持MD文档之间链接跳转2022-11-20 - 支持MD文档之间链接跳转2022-11-20
- 支持预览区域选择主题样式2022-12-04 - 支持预览区域选择主题样式2022-12-04
- Gitlab的支持优化2023-02-23
## 国外开源版本弊端: ## 国外开源版本弊端:
- 作者已经不维护了 - 作者已经不维护了
@ -151,6 +152,9 @@ docker run -itd --name stackedit \
- Gitea可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITEA_CLIENT_ID、GITEA_CLIENT_SECRET、GITEA_URL**[如何创建Gitea应用](./docs/部署之Gitea应用创建.md)** - Gitea可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITEA_CLIENT_ID、GITEA_CLIENT_SECRET、GITEA_URL**[如何创建Gitea应用](./docs/部署之Gitea应用创建.md)**
- Gitlab可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITLAB_CLIENT_ID、GITEA_URL **如何创建Gitlab应用(待补充文档)**
特别说明自建的Gitea、Gitlab要能接入stackedit必须支持跨域
## 编译与运行 ## 编译与运行
> 编译运行的nodejs版本选择11.15.0版本 > 编译运行的nodejs版本选择11.15.0版本

View File

@ -18,6 +18,8 @@ awsSecretAccessKey: ""
giteaClientId: "" giteaClientId: ""
giteaClientSecret: "" giteaClientSecret: ""
giteaUrl: "" giteaUrl: ""
gitlabClientId: ""
gitlabUrl: ""
replicaCount: 1 replicaCount: 1

View File

@ -12,4 +12,6 @@ module.exports = merge(prodEnv, {
// GITEA_CLIENT_ID: '"fe30f8f9-b1e8-4531-8f72-c1a5d3912805"', // GITEA_CLIENT_ID: '"fe30f8f9-b1e8-4531-8f72-c1a5d3912805"',
// GITEA_CLIENT_SECRET: '"lus7oMnb3H6M1hsChndphArE20Txr7erwJLf7SDBQWTw"', // GITEA_CLIENT_SECRET: '"lus7oMnb3H6M1hsChndphArE20Txr7erwJLf7SDBQWTw"',
// GITEA_URL: '"https://gitea.test.com"', // GITEA_URL: '"https://gitea.test.com"',
// GITLAB_CLIENT_ID: '"33e01128c27fe75df3e5b35218d710c7df280e6ee9c90b6ca27ac9d9fdfb92f7"',
// GITLAB_URL: '"http://gitlab.qicoder.com"',
}) })

View File

@ -1,6 +1,6 @@
{ {
"name": "stackedit", "name": "stackedit",
"version": "5.15.17", "version": "5.15.18",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器", "description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁", "author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@ -14,6 +14,8 @@ const wordpressClientId = process.env.WORDPRESS_CLIENT_ID;
const giteaClientId = process.env.GITEA_CLIENT_ID; const giteaClientId = process.env.GITEA_CLIENT_ID;
const giteaClientSecret = process.env.GITEA_CLIENT_SECRET; const giteaClientSecret = process.env.GITEA_CLIENT_SECRET;
const giteaUrl = process.env.GITEA_URL; const giteaUrl = process.env.GITEA_URL;
const gitlabClientId = process.env.GITLAB_CLIENT_ID;
const gitlabUrl = process.env.GITLAB_URL;
exports.values = { exports.values = {
pandocPath, pandocPath,
@ -43,4 +45,6 @@ exports.publicValues = {
allowSponsorship: !!paypalReceiverEmail, allowSponsorship: !!paypalReceiverEmail,
giteaClientId, giteaClientId,
giteaUrl, giteaUrl,
gitlabClientId,
gitlabUrl,
}; };

View File

@ -41,6 +41,7 @@
import modalTemplate from '../common/modalTemplate'; import modalTemplate from '../common/modalTemplate';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
import store from '../../../store'; import store from '../../../store';
import networkSvc from '../../../services/networkSvc';
export default modalTemplate({ export default modalTemplate({
data: () => ({ data: () => ({
@ -65,6 +66,9 @@ export default modalTemplate({
return !!confClientId && !!confServerUrl; return !!confClientId && !!confServerUrl;
}, },
}, },
mounted() {
networkSvc.getServerConf();
},
methods: { methods: {
resolve() { resolve() {
if (this.useServerConf) { if (this.useServerConf) {

View File

@ -5,11 +5,15 @@
<icon-provider provider-id="gitlab"></icon-provider> <icon-provider provider-id="gitlab"></icon-provider>
</div> </div>
<p>将您的<b>GitLab</b>链接到<b>StackEdit中文版</b></p> <p>将您的<b>GitLab</b>链接到<b>StackEdit中文版</b></p>
<template v-if="!useServerConf">
<form-entry label="GitLab URL" error="serverUrl"> <form-entry label="GitLab URL" error="serverUrl">
<input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl"> <input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl">
<input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()"> <input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
<b>例如:</b> https://gitlab.example.com/ <b>例如:</b> https://gitlab.example.com/
<span v-if="httpAppUrl">
非https的URL请跳转到 <a :href="httpAppUrl" target="_blank">HTTP链接</a> 添加Gitlab
</span>
</div> </div>
</form-entry> </form-entry>
<form-entry label="Application ID" error="applicationId"> <form-entry label="Application ID" error="applicationId">
@ -21,6 +25,7 @@
<a href="https://docs.gitlab.com/ee/integration/oauth_provider.html" target="_blank">更多信息</a> <a href="https://docs.gitlab.com/ee/integration/oauth_provider.html" target="_blank">更多信息</a>
</div> </div>
</form-entry> </form-entry>
</template>
</div> </div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button> <button class="button" @click="config.reject()">取消</button>
@ -32,6 +37,8 @@
<script> <script>
import modalTemplate from '../common/modalTemplate'; import modalTemplate from '../common/modalTemplate';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
import store from '../../../store';
import networkSvc from '../../../services/networkSvc';
export default modalTemplate({ export default modalTemplate({
data: () => ({ data: () => ({
@ -41,8 +48,29 @@ export default modalTemplate({
serverUrl: 'gitlabServerUrl', serverUrl: 'gitlabServerUrl',
applicationId: 'gitlabApplicationId', applicationId: 'gitlabApplicationId',
}, },
computed: {
httpAppUrl() {
if (constants.origin.indexOf('https://') === 0 && this.serverUrl.indexOf('http://') === 0) {
return `${constants.origin.replace('https://', 'http://')}/app`;
}
return null;
},
// 使
useServerConf() {
const confClientId = store.getters['data/serverConf'].gitlabClientId;
const confServerUrl = store.getters['data/serverConf'].gitlabUrl;
return !!confClientId && !!confServerUrl;
},
},
mounted() {
networkSvc.getServerConf();
},
methods: { methods: {
resolve() { resolve() {
if (this.useServerConf) {
this.config.resolve({});
return;
}
const serverUrl = this.config.forceServerUrl || this.serverUrl; const serverUrl = this.config.forceServerUrl || this.serverUrl;
if (!serverUrl) { if (!serverUrl) {
this.setError('serverUrl'); this.setError('serverUrl');
@ -51,7 +79,7 @@ export default modalTemplate({
this.setError('applicationId'); this.setError('applicationId');
} }
if (serverUrl && this.applicationId) { if (serverUrl && this.applicationId) {
const parsedUrl = serverUrl.match(/^(https:\/\/[^/]+)/); const parsedUrl = serverUrl.match(/^(http[s]?:\/\/[^/]+)/);
if (!parsedUrl) { if (!parsedUrl) {
this.setError('serverUrl'); this.setError('serverUrl');
} else { } else {

View File

@ -173,6 +173,18 @@ export default new Provider({
}, },
}; };
}, },
async downloadFile({ token, path }) {
const { sha, data } = await gitlabHelper.downloadFile({
...store.getters['workspace/currentWorkspace'],
token,
path,
isImg: true,
});
return {
content: data,
sha,
};
},
async downloadWorkspaceData({ token, syncData }) { async downloadWorkspaceData({ token, syncData }) {
if (!syncData) { if (!syncData) {
return {}; return {};
@ -200,18 +212,27 @@ export default new Provider({
file, file,
commitMessage, commitMessage,
}) { }) {
const isImg = file.type === 'img';
const path = store.getters.gitPathsByItemId[file.id]; const path = store.getters.gitPathsByItemId[file.id];
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`; const absolutePath = !isImg ? `${store.getters['workspace/currentWorkspace'].path || ''}${path}` : file.path;
const sha = gitWorkspaceSvc.shaByPath[path]; const sha = gitWorkspaceSvc.shaByPath[!isImg ? path : file.path];
await gitlabHelper.uploadFile({ await gitlabHelper.uploadFile({
...store.getters['workspace/currentWorkspace'], ...store.getters['workspace/currentWorkspace'],
token, token,
path: absolutePath, path: absolutePath,
content: Provider.serializeContent(content), content: !isImg ? Provider.serializeContent(content) : file.content,
sha, sha,
isImg,
commitMessage, commitMessage,
}); });
if (isImg) {
const res2 = await this.downloadFile({ token, path: absolutePath });
return {
sha: res2.sha,
};
}
// Return new sync data // Return new sync data
return { return {
contentSyncData: { contentSyncData: {

View File

@ -51,11 +51,22 @@ export default {
* https://docs.gitlab.com/ee/api/oauth2.html * https://docs.gitlab.com/ee/api/oauth2.html
*/ */
async startOauth2(serverUrl, applicationId, sub = null, silent = false) { async startOauth2(serverUrl, applicationId, sub = null, silent = false) {
let apiUrl = serverUrl;
let clientId = applicationId;
// 获取gitea配置的参数
await networkSvc.getServerConf();
const confClientId = store.getters['data/serverConf'].gitlabClientId;
const confServerUrl = store.getters['data/serverConf'].gitlabUrl;
// 存在gitea配置则使用后端配置
if (confClientId && confServerUrl) {
apiUrl = confServerUrl;
clientId = confClientId;
}
// Get an OAuth2 code // Get an OAuth2 code
const { accessToken } = await networkSvc.startOauth2( const { accessToken } = await networkSvc.startOauth2(
`${serverUrl}/oauth/authorize`, `${apiUrl}/oauth/authorize`,
{ {
client_id: applicationId, client_id: clientId,
response_type: 'token', response_type: 'token',
scope: 'api', scope: 'api',
}, },
@ -157,14 +168,20 @@ export default {
path, path,
content, content,
sha, sha,
isImg,
commitMessage, commitMessage,
}) { }) {
let uploadContent = content;
if (isImg && typeof content !== 'string') {
uploadContent = await utils.encodeFiletoBase64(content);
}
return request(token, { return request(token, {
method: sha ? 'PUT' : 'POST', method: sha ? 'PUT' : 'POST',
url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`, url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`,
body: { body: {
commit_message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path), commit_message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
content, encoding: 'base64',
content: isImg ? uploadContent : utils.encodeBase64(content),
last_commit_id: sha, last_commit_id: sha,
branch, branch,
}, },
@ -200,6 +217,7 @@ export default {
projectId, projectId,
branch, branch,
path, path,
isImg,
}) { }) {
const res = await request(token, { const res = await request(token, {
url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`, url: `projects/${encodeURIComponent(projectId)}/repository/files/${encodeURIComponent(path)}`,
@ -207,7 +225,7 @@ export default {
}); });
return { return {
sha: res.last_commit_id, sha: res.last_commit_id,
data: utils.decodeBase64(res.content), data: !isImg ? utils.decodeBase64(res.content) : res.content,
}; };
}, },
}; };

View File

@ -317,7 +317,7 @@ export default {
}; };
}, },
parseGitlabProjectPath(url) { parseGitlabProjectPath(url) {
const parsedProject = url && url.match(/^https:\/\/[^/]+\/(.+?)(?:\.git|\/)?$/); const parsedProject = url && url.match(/^http[s]?:\/\/[^/]+\/(.+?)(?:\.git|\/)?$/);
return parsedProject && parsedProject[1]; return parsedProject && parsedProject[1];
}, },
parseGiteaProjectPath(url) { parseGiteaProjectPath(url) {