支持Gitee 修复Github Oauth2授权bug
This commit is contained in:
parent
46383b5b6a
commit
e7e335d958
@ -1,4 +1,4 @@
|
||||
FROM benweet/stackedit-base
|
||||
FROM node:11.15.0
|
||||
|
||||
RUN mkdir -p /opt/stackedit
|
||||
WORKDIR /opt/stackedit
|
||||
|
14
README.md
14
README.md
@ -1,10 +1,22 @@
|
||||
# StackEdit
|
||||
|
||||
从 [StackEdit 官方](https://github.com/benweet/stackedit) fork出来,然后加上了 **Gitee** 的支持,并且已经重新打了镜像,以下官方的部署方式,除了Docker镜像地址不同,其他均一致。
|
||||
|
||||
Fork出来修改的原因:Stackedit的作者可能因为什么原因,已经很久不维护了,Github授权登录很早之前就登录不了了,并且还没发支持国内常用的Gitee,比较蛋疼,所以想到Fork出来改,大概花了周末一整天终于改好了。
|
||||
|
||||
新的Docker镜像在中央仓库为:mafgwo/stackedit,当前最新版本为:5.15.1(延续原有版本号)
|
||||
|
||||
并增加了以下三个环境变量:
|
||||
- `GITEE_CLIENT_ID` Gitee 的 Client ID
|
||||
- `GITEE_CLIENT_SECRET` Gitee 的 Client Secret
|
||||
- `GITEE_CALLBACK` Gitee的回调地址,Gitee授权获取token时还需要传入回调地址,格式是 http[s]://[hostname]:[port]/oauth2/callback
|
||||
|
||||
|
||||
[![Build Status](https://img.shields.io/travis/benweet/stackedit.svg?style=flat)](https://travis-ci.org/benweet/stackedit) [![NPM version](https://img.shields.io/npm/v/stackedit.svg?style=flat)](https://www.npmjs.org/package/stackedit)
|
||||
|
||||
> Full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.
|
||||
|
||||
https://stackedit.io/
|
||||
https://edit.qicoder.com/
|
||||
|
||||
### Ecosystem
|
||||
|
||||
|
@ -8,6 +8,8 @@ googleClientId: ""
|
||||
googleApiKey: ""
|
||||
githubClientId: ""
|
||||
githubClientSecret: ""
|
||||
giteeClientId: ""
|
||||
giteeClientSecret: ""
|
||||
wordpressClientId: ""
|
||||
wordpressSecret: ""
|
||||
paypalReceiverEmail: ""
|
||||
|
@ -15,10 +15,10 @@
|
||||
},
|
||||
"app": {
|
||||
"urls": [
|
||||
"https://stackedit.io/"
|
||||
"https://edit.qicoder.com/"
|
||||
],
|
||||
"launch": {
|
||||
"web_url": "https://stackedit.io/app"
|
||||
"web_url": "https://edit.qicoder.com/app"
|
||||
}
|
||||
},
|
||||
"offline_enabled": true,
|
||||
|
@ -23,7 +23,7 @@ module.exports = {
|
||||
},
|
||||
dev: {
|
||||
env: require('./dev.env'),
|
||||
port: 8080,
|
||||
port: 80,
|
||||
autoOpenBrowser: false,
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
|
13
index.html
13
index.html
@ -3,15 +3,26 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>StackEdit</title>
|
||||
<link rel="canonical" href="https://stackedit.io/app">
|
||||
<link rel="canonical" href="https://edit.qicoder.com/app">
|
||||
<meta name="description" content="Free, open-source, full-featured Markdown editor.">
|
||||
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
<script>
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?6e5d2dbd2eeb7bba778f1056fba280d1";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "stackedit",
|
||||
"version": "5.14.10",
|
||||
"version": "5.15.1",
|
||||
"description": "Free, open-source, full-featured Markdown editor",
|
||||
"author": "Benoit Schweblin",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/benweet/stackedit/issues"
|
||||
"url": "https://github.com/mafgwo/stackedit/issues"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -8,6 +8,9 @@ const dropboxAppKey = process.env.DROPBOX_APP_KEY;
|
||||
const dropboxAppKeyFull = process.env.DROPBOX_APP_KEY_FULL;
|
||||
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
||||
const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
|
||||
const giteeClientId = process.env.GITEE_CLIENT_ID;
|
||||
const giteeClientSecret = process.env.GITEE_CLIENT_SECRET;
|
||||
const giteeCallback = process.env.GITEE_CALLBACK;
|
||||
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
||||
const googleApiKey = process.env.GOOGLE_API_KEY;
|
||||
const wordpressClientId = process.env.WORDPRESS_CLIENT_ID;
|
||||
@ -22,6 +25,9 @@ exports.values = {
|
||||
dropboxAppKeyFull,
|
||||
githubClientId,
|
||||
githubClientSecret,
|
||||
giteeClientId,
|
||||
giteeClientSecret,
|
||||
giteeCallback,
|
||||
googleClientId,
|
||||
googleApiKey,
|
||||
wordpressClientId,
|
||||
@ -31,6 +37,7 @@ exports.publicValues = {
|
||||
dropboxAppKey,
|
||||
dropboxAppKeyFull,
|
||||
githubClientId,
|
||||
giteeClientId,
|
||||
googleClientId,
|
||||
googleApiKey,
|
||||
wordpressClientId,
|
||||
|
45
server/gitee.js
Normal file
45
server/gitee.js
Normal file
@ -0,0 +1,45 @@
|
||||
const qs = require('qs'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const request = require('request');
|
||||
const conf = require('./conf');
|
||||
|
||||
function giteeToken(clientId, code) {
|
||||
console.log('clientId: ' + clientId);
|
||||
console.log('code: ' + code);
|
||||
console.log('client_secret: ' + conf.values.giteeClientSecret);
|
||||
console.log('redirect_uri: ' + conf.values.giteeCallback);
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
method: 'POST',
|
||||
url: 'https://gitee.com/oauth/token',
|
||||
form: {
|
||||
client_id: clientId,
|
||||
client_secret: conf.values.giteeClientSecret,
|
||||
code,
|
||||
grant_type: 'authorization_code',
|
||||
scope: 'authorization_code',
|
||||
redirect_uri: conf.values.giteeCallback,
|
||||
},
|
||||
json: true
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
const token = body.access_token;
|
||||
if (token) {
|
||||
resolve(token);
|
||||
} else {
|
||||
reject(res.statusCode + ',body:' + JSON.stringify(body));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.giteeToken = (req, res) => {
|
||||
giteeToken(req.query.clientId, req.query.code)
|
||||
.then(
|
||||
token => res.send(token),
|
||||
err => res
|
||||
.status(400)
|
||||
.send(err ? err.message || err.toString() : 'bad_code'),
|
||||
);
|
||||
};
|
@ -4,6 +4,7 @@ const bodyParser = require('body-parser');
|
||||
const path = require('path');
|
||||
const user = require('./user');
|
||||
const github = require('./github');
|
||||
const gitee = require('./gitee');
|
||||
const pdf = require('./pdf');
|
||||
const pandoc = require('./pandoc');
|
||||
const conf = require('./conf');
|
||||
@ -25,6 +26,7 @@ module.exports = (app) => {
|
||||
}
|
||||
|
||||
app.get('/oauth2/githubToken', github.githubToken);
|
||||
app.get('/oauth2/giteeToken', gitee.giteeToken);
|
||||
app.get('/conf', (req, res) => res.send(conf.publicValues));
|
||||
app.get('/userInfo', user.userInfo);
|
||||
app.post('/pdfExport', pdf.generate);
|
||||
@ -37,6 +39,8 @@ module.exports = (app) => {
|
||||
app.get('/', (req, res) => res.sendFile(resolvePath('static/landing/index.html')));
|
||||
// Serve sitemap.xml
|
||||
app.get('/sitemap.xml', (req, res) => res.sendFile(resolvePath('static/sitemap.xml')));
|
||||
// Serve google-api.js
|
||||
app.get('/google-api.js', (req, res) => res.sendFile(resolvePath('static/google-api.js')));
|
||||
// Serve callback.html
|
||||
app.get('/oauth2/callback', (req, res) => res.sendFile(resolvePath('static/oauth2/callback.html')));
|
||||
// Google Drive action receiver
|
||||
|
5
src/assets/iconGitee.svg
Normal file
5
src/assets/iconGitee.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1652950823759" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2991" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs><style type="text/css"></style></defs>
|
||||
<path d="M512 1024C229.222 1024 0 794.778 0 512S229.222 0 512 0s512 229.222 512 512-229.222 512-512 512z m259.149-568.883h-290.74a25.293 25.293 0 0 0-25.292 25.293l-0.026 63.206c0 13.952 11.315 25.293 25.267 25.293h177.024c13.978 0 25.293 11.315 25.293 25.267v12.646a75.853 75.853 0 0 1-75.853 75.853h-240.23a25.293 25.293 0 0 1-25.267-25.293V417.203a75.853 75.853 0 0 1 75.827-75.853h353.946a25.293 25.293 0 0 0 25.267-25.292l0.077-63.207a25.293 25.293 0 0 0-25.268-25.293H417.152a189.62 189.62 0 0 0-189.62 189.645V771.15c0 13.977 11.316 25.293 25.294 25.293h372.94a170.65 170.65 0 0 0 170.65-170.65V480.384a25.293 25.293 0 0 0-25.293-25.267z" fill="#C71D23" p-id="2992"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="modal" v-if="config" @keydown.esc.stop="onEscape" @keydown.tab="onTab" @focusin="onFocusInOut" @focusout="onFocusInOut">
|
||||
<div class="modal__sponsor-banner" v-if="!isSponsor">
|
||||
StackEdit is <a class="not-tabbable" target="_blank" href="https://github.com/benweet/stackedit/">open source</a>, please consider
|
||||
StackEdit is <a class="not-tabbable" target="_blank" href="https://github.com/mafgwo/stackedit/">open source</a>, please consider
|
||||
<a class="not-tabbable" href="javascript:void(0)" @click="sponsor">sponsoring</a> for just $5.
|
||||
</div>
|
||||
<component v-if="currentModalComponent" :is="currentModalComponent"></component>
|
||||
@ -56,6 +56,11 @@ import GithubWorkspaceModal from './modals/providers/GithubWorkspaceModal';
|
||||
import GithubPublishModal from './modals/providers/GithubPublishModal';
|
||||
import GistSyncModal from './modals/providers/GistSyncModal';
|
||||
import GistPublishModal from './modals/providers/GistPublishModal';
|
||||
import GiteeAccountModal from './modals/providers/GiteeAccountModal';
|
||||
import GiteeOpenModal from './modals/providers/GiteeOpenModal';
|
||||
import GiteeSaveModal from './modals/providers/GiteeSaveModal';
|
||||
import GiteeWorkspaceModal from './modals/providers/GiteeWorkspaceModal';
|
||||
import GiteePublishModal from './modals/providers/GiteePublishModal';
|
||||
import GitlabAccountModal from './modals/providers/GitlabAccountModal';
|
||||
import GitlabOpenModal from './modals/providers/GitlabOpenModal';
|
||||
import GitlabPublishModal from './modals/providers/GitlabPublishModal';
|
||||
@ -107,6 +112,11 @@ export default {
|
||||
GithubPublishModal,
|
||||
GistSyncModal,
|
||||
GistPublishModal,
|
||||
GiteeAccountModal,
|
||||
GiteeOpenModal,
|
||||
GiteeSaveModal,
|
||||
GiteeWorkspaceModal,
|
||||
GiteePublishModal,
|
||||
GitlabAccountModal,
|
||||
GitlabOpenModal,
|
||||
GitlabPublishModal,
|
||||
|
@ -23,6 +23,9 @@
|
||||
<span v-else-if="currentWorkspace.providerId === 'githubWorkspace'">
|
||||
<b>{{currentWorkspace.name}}</b> synced with a <a :href="workspaceLocationUrl" target="_blank">GitHub repo</a>.
|
||||
</span>
|
||||
<span v-else-if="currentWorkspace.providerId === 'giteeWorkspace'">
|
||||
<b>{{currentWorkspace.name}}</b> synced with a <a :href="workspaceLocationUrl" target="_blank">Gitee repo</a>.
|
||||
</span>
|
||||
<span v-else-if="currentWorkspace.providerId === 'gitlabWorkspace'">
|
||||
<b>{{currentWorkspace.name}}</b> synced with a <a :href="workspaceLocationUrl" target="_blank">GitLab project</a>.
|
||||
</span>
|
||||
|
@ -52,6 +52,13 @@
|
||||
<span>{{token.name}}</span>
|
||||
</menu-entry>
|
||||
</div>
|
||||
<div v-for="token in giteeTokens" :key="token.sub">
|
||||
<menu-entry @click.native="publishGitee(token)">
|
||||
<icon-provider slot="icon" provider-id="gitee"></icon-provider>
|
||||
<div>Publish to Gitee</div>
|
||||
<span>{{token.name}}</span>
|
||||
</menu-entry>
|
||||
</div>
|
||||
<div v-for="token in gitlabTokens" :key="token.sub">
|
||||
<menu-entry @click.native="publishGitlab(token)">
|
||||
<icon-provider slot="icon" provider-id="gitlab"></icon-provider>
|
||||
@ -93,6 +100,10 @@
|
||||
<icon-provider slot="icon" provider-id="github"></icon-provider>
|
||||
<span>Add GitHub account</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGiteeAccount">
|
||||
<icon-provider slot="icon" provider-id="gitee"></icon-provider>
|
||||
<span>Add Gitee account</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGitlabAccount">
|
||||
<icon-provider slot="icon" provider-id="gitlab"></icon-provider>
|
||||
<span>Add GitLab account</span>
|
||||
@ -119,6 +130,7 @@ import MenuEntry from './common/MenuEntry';
|
||||
import googleHelper from '../../services/providers/helpers/googleHelper';
|
||||
import dropboxHelper from '../../services/providers/helpers/dropboxHelper';
|
||||
import githubHelper from '../../services/providers/helpers/githubHelper';
|
||||
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
||||
import gitlabHelper from '../../services/providers/helpers/gitlabHelper';
|
||||
import wordpressHelper from '../../services/providers/helpers/wordpressHelper';
|
||||
import zendeskHelper from '../../services/providers/helpers/zendeskHelper';
|
||||
@ -168,6 +180,9 @@ export default {
|
||||
githubTokens() {
|
||||
return tokensToArray(store.getters['data/githubTokensBySub']);
|
||||
},
|
||||
giteeTokens() {
|
||||
return tokensToArray(store.getters['data/giteeTokensBySub']);
|
||||
},
|
||||
gitlabTokens() {
|
||||
return tokensToArray(store.getters['data/gitlabTokensBySub']);
|
||||
},
|
||||
@ -218,6 +233,12 @@ export default {
|
||||
await githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess);
|
||||
} catch (e) { /* cancel */ }
|
||||
},
|
||||
async addGiteeAccount() {
|
||||
try {
|
||||
await store.dispatch('modal/open', { type: 'giteeAccount' });
|
||||
await giteeHelper.addAccount();
|
||||
} catch (e) { /* cancel */ }
|
||||
},
|
||||
async addGitlabAccount() {
|
||||
try {
|
||||
const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
|
||||
@ -245,6 +266,7 @@ export default {
|
||||
publishBloggerPage: publishModalOpener('bloggerPagePublish', 'publishToBloggerPage'),
|
||||
publishDropbox: publishModalOpener('dropboxPublish', 'publishToDropbox'),
|
||||
publishGithub: publishModalOpener('githubPublish', 'publishToGithub'),
|
||||
publishGitee: publishModalOpener('giteePublish', 'publishToGitee'),
|
||||
publishGist: publishModalOpener('gistPublish', 'publishToGist'),
|
||||
publishGitlab: publishModalOpener('gitlabPublish', 'publishToGitlab'),
|
||||
publishGoogleDrive: publishModalOpener('googleDrivePublish', 'publishToGoogleDrive'),
|
||||
|
@ -83,6 +83,10 @@
|
||||
<icon-provider slot="icon" provider-id="github"></icon-provider>
|
||||
<span>Add GitHub account</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGiteeAccount">
|
||||
<icon-provider slot="icon" provider-id="gitee"></icon-provider>
|
||||
<span>Add Gitee account</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGitlabAccount">
|
||||
<icon-provider slot="icon" provider-id="gitlab"></icon-provider>
|
||||
<span>Add GitLab account</span>
|
||||
@ -101,6 +105,7 @@ import MenuEntry from './common/MenuEntry';
|
||||
import googleHelper from '../../services/providers/helpers/googleHelper';
|
||||
import dropboxHelper from '../../services/providers/helpers/dropboxHelper';
|
||||
import githubHelper from '../../services/providers/helpers/githubHelper';
|
||||
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
||||
import gitlabHelper from '../../services/providers/helpers/gitlabHelper';
|
||||
import googleDriveProvider from '../../services/providers/googleDriveProvider';
|
||||
import dropboxProvider from '../../services/providers/dropboxProvider';
|
||||
@ -148,6 +153,9 @@ export default {
|
||||
githubTokens() {
|
||||
return tokensToArray(store.getters['data/githubTokensBySub']);
|
||||
},
|
||||
giteeTokens() {
|
||||
return tokensToArray(store.getters['data/giteeTokensBySub']);
|
||||
},
|
||||
gitlabTokens() {
|
||||
return tokensToArray(store.getters['data/gitlabTokensBySub']);
|
||||
},
|
||||
@ -157,7 +165,8 @@ export default {
|
||||
noToken() {
|
||||
return !this.googleDriveTokens.length
|
||||
&& !this.dropboxTokens.length
|
||||
&& !this.githubTokens.length;
|
||||
&& !this.githubTokens.length
|
||||
&& !this.giteeTokens.length;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -183,6 +192,12 @@ export default {
|
||||
await githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess);
|
||||
} catch (e) { /* cancel */ }
|
||||
},
|
||||
async addGiteeAccount() {
|
||||
try {
|
||||
await store.dispatch('modal/open', { type: 'giteeAccount' });
|
||||
await giteeHelper.addAccount();
|
||||
} catch (e) { /* cancel */ }
|
||||
},
|
||||
async addGitlabAccount() {
|
||||
try {
|
||||
const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
|
||||
|
@ -21,6 +21,10 @@
|
||||
<icon-provider slot="icon" provider-id="githubWorkspace"></icon-provider>
|
||||
<span>Add a <b>GitHub</b> workspace</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGiteeWorkspace">
|
||||
<icon-provider slot="icon" provider-id="giteeWorkspace"></icon-provider>
|
||||
<span>Add a <b>Gitee</b> workspace</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGitlabWorkspace">
|
||||
<icon-provider slot="icon" provider-id="gitlabWorkspace"></icon-provider>
|
||||
<span>Add a <b>GitLab</b> workspace</span>
|
||||
@ -67,6 +71,13 @@ export default {
|
||||
});
|
||||
} catch (e) { /* Cancel */ }
|
||||
},
|
||||
async addGiteeWorkspace() {
|
||||
try {
|
||||
store.dispatch('modal/open', {
|
||||
type: 'giteeWorkspace',
|
||||
});
|
||||
} catch (e) { /* Cancel */ }
|
||||
},
|
||||
async addGitlabWorkspace() {
|
||||
try {
|
||||
const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
|
||||
|
@ -2,9 +2,9 @@
|
||||
<modal-inner class="modal__inner-1--about-modal" aria-label="About">
|
||||
<div class="modal__content">
|
||||
<div class="logo-background"></div>
|
||||
StackEdit on <a target="_blank" href="https://github.com/benweet/stackedit/">GitHub</a>
|
||||
StackEdit on <a target="_blank" href="https://github.com/mafgwo/stackedit/">GitHub</a>
|
||||
<br>
|
||||
<a target="_blank" href="https://github.com/benweet/stackedit/issues">Issue tracker</a> — <a target="_blank" href="https://github.com/benweet/stackedit/releases">Changelog</a>
|
||||
<a target="_blank" href="https://github.com/mafgwo/stackedit/issues">Issue tracker</a> — <a target="_blank" href="https://github.com/mafgwo/stackedit/releases">Changelog</a>
|
||||
<br>
|
||||
<a target="_blank" href="https://chrome.google.com/webstore/detail/iiooodelglhkcpgbajoejffhijaclcdg">Chrome app</a> — <a target="_blank" href="https://chrome.google.com/webstore/detail/ajehldoplanpchfokmeempkekhnhmoha">Chrome extension</a>
|
||||
<br>
|
||||
@ -12,7 +12,7 @@
|
||||
<br>
|
||||
StackEdit on <a target="_blank" href="https://twitter.com/stackedit/">Twitter</a>
|
||||
<hr>
|
||||
<small>© 2013-2019 Dock5 Software Ltd.<br>v{{version}}</small>
|
||||
<small>© 2013-2022 Dock5 Software Ltd.<br>v{{version}}</small>
|
||||
<h3>FAQ</h3>
|
||||
<div class="faq" v-html="faq"></div>
|
||||
<div class="modal__info">
|
||||
|
@ -49,6 +49,10 @@
|
||||
<icon-provider slot="icon" provider-id="github"></icon-provider>
|
||||
<span>Add GitHub account</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGiteeAccount">
|
||||
<icon-provider slot="icon" provider-id="gitee"></icon-provider>
|
||||
<span>Add Gitee account</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="addGitlabAccount">
|
||||
<icon-provider slot="icon" provider-id="gitlab"></icon-provider>
|
||||
<span>Add GitLab account</span>
|
||||
@ -85,6 +89,7 @@ import utils from '../../services/utils';
|
||||
import googleHelper from '../../services/providers/helpers/googleHelper';
|
||||
import dropboxHelper from '../../services/providers/helpers/dropboxHelper';
|
||||
import githubHelper from '../../services/providers/helpers/githubHelper';
|
||||
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
||||
import gitlabHelper from '../../services/providers/helpers/gitlabHelper';
|
||||
import wordpressHelper from '../../services/providers/helpers/wordpressHelper';
|
||||
import zendeskHelper from '../../services/providers/helpers/zendeskHelper';
|
||||
@ -128,6 +133,13 @@ export default {
|
||||
name: token.name,
|
||||
scopes: token.scopes,
|
||||
})),
|
||||
...Object.values(store.getters['data/giteeTokensBySub']).map(token => ({
|
||||
token,
|
||||
providerId: 'gitee',
|
||||
userId: token.sub,
|
||||
name: token.name,
|
||||
scopes: ['projects', 'pull_requests'],
|
||||
})),
|
||||
...Object.values(store.getters['data/gitlabTokensBySub']).map(token => ({
|
||||
token,
|
||||
providerId: 'gitlab',
|
||||
@ -180,6 +192,12 @@ export default {
|
||||
await githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess);
|
||||
} catch (e) { /* cancel */ }
|
||||
},
|
||||
async addGiteeAccount() {
|
||||
try {
|
||||
await store.dispatch('modal/open', { type: 'giteeAccount' });
|
||||
await giteeHelper.addAccount();
|
||||
} catch (e) { /* cancel */ }
|
||||
},
|
||||
async addGitlabAccount() {
|
||||
try {
|
||||
const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
|
||||
|
@ -72,7 +72,7 @@
|
||||
<span class="token key atrule">katex</span><span class="token punctuation">:</span>
|
||||
<span class="token key atrule">enabled</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
|
||||
</code></pre>
|
||||
<p>For the full list of options, see <a href="https://github.com/benweet/stackedit/blob/master/src/data/presets.js" target="_blank">here</a>.</p>
|
||||
<p>For the full list of options, see <a href="https://github.com/mafgwo/stackedit/blob/master/src/data/presets.js" target="_blank">here</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,7 +45,7 @@ export default modalTemplate({
|
||||
},
|
||||
methods: {
|
||||
resolve(evt) {
|
||||
evt.preventDefault(); // Fixes https://github.com/benweet/stackedit/issues/1503
|
||||
evt.preventDefault(); // Fixes https://github.com/mafgwo/stackedit/issues/1503
|
||||
if (!this.url) {
|
||||
this.setError('url');
|
||||
} else {
|
||||
|
@ -22,7 +22,7 @@ export default modalTemplate({
|
||||
}),
|
||||
methods: {
|
||||
resolve(evt) {
|
||||
evt.preventDefault(); // Fixes https://github.com/benweet/stackedit/issues/1503
|
||||
evt.preventDefault(); // Fixes https://github.com/mafgwo/stackedit/issues/1503
|
||||
if (!this.url) {
|
||||
this.setError('url');
|
||||
} else {
|
||||
|
31
src/components/modals/providers/GiteeAccountModal.vue
Normal file
31
src/components/modals/providers/GiteeAccountModal.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<modal-inner aria-label="Link Gitee account">
|
||||
<div class="modal__content">
|
||||
<div class="modal__image">
|
||||
<icon-provider provider-id="gitee"></icon-provider>
|
||||
</div>
|
||||
<p>Link your <b>Gitee</b> account to <b>StackEdit</b>.</p>
|
||||
<div class="form-entry">
|
||||
<div class="form-entry__checkbox">
|
||||
<label>
|
||||
<input type="checkbox" v-model="repoFullAccess"> Grant access to your private repositories
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal__button-bar">
|
||||
<button class="button" @click="config.reject()">Cancel</button>
|
||||
<button class="button button--resolve" @click="config.resolve()">Ok</button>
|
||||
</div>
|
||||
</modal-inner>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modalTemplate from '../common/modalTemplate';
|
||||
|
||||
export default modalTemplate({
|
||||
computedLocalSettings: {
|
||||
repoFullAccess: 'giteeRepoFullAccess',
|
||||
},
|
||||
});
|
||||
</script>
|
70
src/components/modals/providers/GiteeOpenModal.vue
Normal file
70
src/components/modals/providers/GiteeOpenModal.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<modal-inner aria-label="Synchronize with Gitee">
|
||||
<div class="modal__content">
|
||||
<div class="modal__image">
|
||||
<icon-provider provider-id="gitee"></icon-provider>
|
||||
</div>
|
||||
<p>Open a file from your <b>Gitee</b> repository and keep it synced.</p>
|
||||
<form-entry label="Repository URL" error="repoUrl">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> https://gitee.com/owner/my-repo
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="File path" error="path">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="path" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> path/to/README.md
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="Branch" info="optional">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="branch" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
If not supplied, the <code>master</code> branch will be used.
|
||||
</div>
|
||||
</form-entry>
|
||||
</div>
|
||||
<div class="modal__button-bar">
|
||||
<button class="button" @click="config.reject()">Cancel</button>
|
||||
<button class="button button--resolve" @click="resolve()">Ok</button>
|
||||
</div>
|
||||
</modal-inner>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import giteeProvider from '../../../services/providers/giteeProvider';
|
||||
import modalTemplate from '../common/modalTemplate';
|
||||
import utils from '../../../services/utils';
|
||||
|
||||
export default modalTemplate({
|
||||
data: () => ({
|
||||
branch: '',
|
||||
path: '',
|
||||
}),
|
||||
computedLocalSettings: {
|
||||
repoUrl: 'giteeRepoUrl',
|
||||
},
|
||||
methods: {
|
||||
resolve() {
|
||||
const parsedRepo = utils.parseGithubRepoUrl(this.repoUrl);
|
||||
if (!parsedRepo) {
|
||||
this.setError('repoUrl');
|
||||
}
|
||||
if (!this.path) {
|
||||
this.setError('path');
|
||||
}
|
||||
if (parsedRepo && this.path) {
|
||||
// Return new location
|
||||
const location = giteeProvider.makeLocation(
|
||||
this.config.token,
|
||||
parsedRepo.owner,
|
||||
parsedRepo.repo,
|
||||
this.branch || 'master',
|
||||
this.path,
|
||||
);
|
||||
this.config.resolve(location);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
86
src/components/modals/providers/GiteePublishModal.vue
Normal file
86
src/components/modals/providers/GiteePublishModal.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<modal-inner aria-label="Publish to Gitee">
|
||||
<div class="modal__content">
|
||||
<div class="modal__image">
|
||||
<icon-provider provider-id="gitee"></icon-provider>
|
||||
</div>
|
||||
<p>Publish <b>{{currentFileName}}</b> to your <b>Gitee</b> repository.</p>
|
||||
<form-entry label="Repository URL" error="repoUrl">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> https://gitee.com/owner/my-repo
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="File path" error="path">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="path" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> path/to/README.md<br>
|
||||
If the file exists, it will be overwritten.
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="Branch" info="optional">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="branch" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
If not supplied, the <code>master</code> branch will be used.
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="Template">
|
||||
<select slot="field" class="textfield" v-model="selectedTemplate" @keydown.enter="resolve()">
|
||||
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id">
|
||||
{{ template.name }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="form-entry__actions">
|
||||
<a href="javascript:void(0)" @click="configureTemplates">Configure templates</a>
|
||||
</div>
|
||||
</form-entry>
|
||||
</div>
|
||||
<div class="modal__button-bar">
|
||||
<button class="button" @click="config.reject()">Cancel</button>
|
||||
<button class="button button--resolve" @click="resolve()">Ok</button>
|
||||
</div>
|
||||
</modal-inner>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import giteeProvider from '../../../services/providers/giteeProvider';
|
||||
import modalTemplate from '../common/modalTemplate';
|
||||
import utils from '../../../services/utils';
|
||||
|
||||
export default modalTemplate({
|
||||
data: () => ({
|
||||
branch: '',
|
||||
path: '',
|
||||
}),
|
||||
computedLocalSettings: {
|
||||
repoUrl: 'giteeRepoUrl',
|
||||
selectedTemplate: 'giteePublishTemplate',
|
||||
},
|
||||
created() {
|
||||
this.path = `${this.currentFileName}.md`;
|
||||
},
|
||||
methods: {
|
||||
resolve() {
|
||||
const parsedRepo = utils.parseGithubRepoUrl(this.repoUrl);
|
||||
if (!parsedRepo) {
|
||||
this.setError('repoUrl');
|
||||
}
|
||||
if (!this.path) {
|
||||
this.setError('path');
|
||||
}
|
||||
if (parsedRepo && this.path) {
|
||||
// Return new location
|
||||
const location = giteeProvider.makeLocation(
|
||||
this.config.token,
|
||||
parsedRepo.owner,
|
||||
parsedRepo.repo,
|
||||
this.branch || 'master',
|
||||
this.path,
|
||||
);
|
||||
location.templateId = this.selectedTemplate;
|
||||
this.config.resolve(location);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
73
src/components/modals/providers/GiteeSaveModal.vue
Normal file
73
src/components/modals/providers/GiteeSaveModal.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<modal-inner aria-label="Synchronize with Gitee">
|
||||
<div class="modal__content">
|
||||
<div class="modal__image">
|
||||
<icon-provider provider-id="gitee"></icon-provider>
|
||||
</div>
|
||||
<p>Save <b>{{currentFileName}}</b> to your <b>Gitee</b> repository and keep it synced.</p>
|
||||
<form-entry label="Repository URL" error="repoUrl">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> https://gitee.com/owner/my-repo
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="File path" error="path">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="path" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> path/to/README.md<br>
|
||||
If the file exists, it will be overwritten.
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="Branch" info="optional">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="branch" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
If not supplied, the <code>master</code> branch will be used.
|
||||
</div>
|
||||
</form-entry>
|
||||
</div>
|
||||
<div class="modal__button-bar">
|
||||
<button class="button" @click="config.reject()">Cancel</button>
|
||||
<button class="button button--resolve" @click="resolve()">Ok</button>
|
||||
</div>
|
||||
</modal-inner>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import giteeProvider from '../../../services/providers/giteeProvider';
|
||||
import modalTemplate from '../common/modalTemplate';
|
||||
import utils from '../../../services/utils';
|
||||
|
||||
export default modalTemplate({
|
||||
data: () => ({
|
||||
branch: '',
|
||||
path: '',
|
||||
}),
|
||||
computedLocalSettings: {
|
||||
repoUrl: 'giteeRepoUrl',
|
||||
},
|
||||
created() {
|
||||
this.path = `${this.currentFileName}.md`;
|
||||
},
|
||||
methods: {
|
||||
resolve() {
|
||||
const parsedRepo = utils.parseGithubRepoUrl(this.repoUrl);
|
||||
if (!parsedRepo) {
|
||||
this.setError('repoUrl');
|
||||
}
|
||||
if (!this.path) {
|
||||
this.setError('path');
|
||||
}
|
||||
if (parsedRepo && this.path) {
|
||||
const location = giteeProvider.makeLocation(
|
||||
this.config.token,
|
||||
parsedRepo.owner,
|
||||
parsedRepo.repo,
|
||||
this.branch || 'master',
|
||||
this.path,
|
||||
);
|
||||
this.config.resolve(location);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
65
src/components/modals/providers/GiteeWorkspaceModal.vue
Normal file
65
src/components/modals/providers/GiteeWorkspaceModal.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<modal-inner aria-label="Synchronize with Gitee">
|
||||
<div class="modal__content">
|
||||
<div class="modal__image">
|
||||
<icon-provider provider-id="gitee"></icon-provider>
|
||||
</div>
|
||||
<p>Create a workspace synced with a <b>Gitee</b> repository folder.</p>
|
||||
<form-entry label="Repository URL" error="repoUrl">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
<b>Example:</b> https://gitee.com/owner/my-repo
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="Folder path" info="optional">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="path" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
If not supplied, the root folder will be used.
|
||||
</div>
|
||||
</form-entry>
|
||||
<form-entry label="Branch" info="optional">
|
||||
<input slot="field" class="textfield" type="text" v-model.trim="branch" @keydown.enter="resolve()">
|
||||
<div class="form-entry__info">
|
||||
If not supplied, the <code>master</code> branch will be used.
|
||||
</div>
|
||||
</form-entry>
|
||||
</div>
|
||||
<div class="modal__button-bar">
|
||||
<button class="button" @click="config.reject()">Cancel</button>
|
||||
<button class="button button--resolve" @click="resolve()">Ok</button>
|
||||
</div>
|
||||
</modal-inner>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from '../../../services/utils';
|
||||
import modalTemplate from '../common/modalTemplate';
|
||||
|
||||
export default modalTemplate({
|
||||
data: () => ({
|
||||
branch: '',
|
||||
path: '',
|
||||
}),
|
||||
computedLocalSettings: {
|
||||
repoUrl: 'giteeWorkspaceRepoUrl',
|
||||
},
|
||||
methods: {
|
||||
resolve() {
|
||||
const parsedRepo = utils.parseGithubRepoUrl(this.repoUrl);
|
||||
if (!parsedRepo) {
|
||||
this.setError('repoUrl');
|
||||
} else {
|
||||
const path = this.path && this.path.replace(/^\//, '');
|
||||
const url = utils.addQueryParams('app', {
|
||||
...parsedRepo,
|
||||
providerId: 'giteeWorkspace',
|
||||
branch: this.branch || 'master',
|
||||
path: path || undefined,
|
||||
}, true);
|
||||
this.config.resolve();
|
||||
window.open(url);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -19,6 +19,8 @@ export default () => ({
|
||||
githubPublishTemplate: 'jekyllSite',
|
||||
gistIsPublic: false,
|
||||
gistPublishTemplate: 'plainText',
|
||||
giteeRepoUrl: '',
|
||||
giteeWorkspaceRepoUrl: '',
|
||||
gitlabServerUrl: '',
|
||||
gitlabApplicationId: '',
|
||||
gitlabProjectUrl: '',
|
||||
|
@ -79,16 +79,16 @@ turndown:
|
||||
|
||||
# GitHub/GitLab commit messages
|
||||
git:
|
||||
createFileMessage: '{{path}} created from https://stackedit.io/'
|
||||
updateFileMessage: '{{path}} updated from https://stackedit.io/'
|
||||
deleteFileMessage: '{{path}} deleted from https://stackedit.io/'
|
||||
createFileMessage: '{{path}} created from https://edit.qicoder.com/'
|
||||
updateFileMessage: '{{path}} updated from https://edit.qicoder.com/'
|
||||
deleteFileMessage: '{{path}} deleted from https://edit.qicoder.com/'
|
||||
|
||||
# Default content for new files
|
||||
newFileContent: |
|
||||
|
||||
|
||||
|
||||
> Written with [StackEdit](https://stackedit.io/).
|
||||
> Written with [StackEdit](https://edit.qicoder.com/).
|
||||
|
||||
# Default properties for new files
|
||||
newFileProperties: |
|
||||
|
@ -6,4 +6,4 @@ We recommend syncing your workspace to make sure files won't be lost in case you
|
||||
|
||||
**Can StackEdit access my data without telling me?**
|
||||
|
||||
StackEdit is a browser-based application. The access tokens issued by Google, Dropbox, GitHub... are stored in your browser and are not sent to any kind of backend or 3^rd^ party so your data won't be accessed by anyone.
|
||||
StackEdit is a browser-based application. The access tokens issued by Google, Dropbox, GitHub, Gitee... are stored in your browser and are not sent to any kind of backend or 3^rd^ party so your data won't be accessed by anyone.
|
||||
|
@ -172,6 +172,11 @@ export default [
|
||||
'GitHub workspace creator',
|
||||
'Use the workspace menu to create a GitHub workspace.',
|
||||
),
|
||||
new Feature(
|
||||
'addGiteeWorkspace',
|
||||
'GitHub workspace creator',
|
||||
'Use the workspace menu to create a Gitee workspace.',
|
||||
),
|
||||
new Feature(
|
||||
'addGitlabWorkspace',
|
||||
'GitLab workspace creator',
|
||||
|
@ -5,7 +5,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{files.0.name}}</title>
|
||||
<link rel="stylesheet" href="https://stackedit.io/style.css" />
|
||||
<link rel="stylesheet" href="https://edit.qicoder.com/style.css" />
|
||||
</head>
|
||||
|
||||
{{#if pdf}}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{files.0.name}}</title>
|
||||
<link rel="stylesheet" href="https://stackedit.io/style.css" />
|
||||
<link rel="stylesheet" href="https://edit.qicoder.com/style.css" />
|
||||
</head>
|
||||
|
||||
{{#if pdf}}
|
||||
|
@ -26,6 +26,8 @@ export default {
|
||||
return 'blogger';
|
||||
case 'couchdbWorkspace':
|
||||
return 'couchdb';
|
||||
case 'giteeWorkspace':
|
||||
return 'gitee';
|
||||
default:
|
||||
return this.providerId;
|
||||
}
|
||||
@ -86,4 +88,8 @@ export default {
|
||||
.icon-provider--couchdb {
|
||||
background-image: url(../assets/iconCouchdb.svg);
|
||||
}
|
||||
|
||||
.icon-provider--gitee {
|
||||
background-image: url(../assets/iconGitee.svg);
|
||||
}
|
||||
</style>
|
||||
|
@ -69,7 +69,7 @@ export default {
|
||||
await new Promise((resolve, reject) => {
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
script.src = `https://apis.google.com/js/api.js?${Date.now()}`;
|
||||
script.src = `https://www.gstatic.cn/charts/loader.js?${Date.now()}`;
|
||||
try {
|
||||
document.head.appendChild(script); // This can fail with bad network
|
||||
timeout = setTimeout(reject, networkTimeout);
|
||||
|
165
src/services/providers/giteeProvider.js
Normal file
165
src/services/providers/giteeProvider.js
Normal file
@ -0,0 +1,165 @@
|
||||
import store from '../../store';
|
||||
import giteeHelper from './helpers/giteeHelper';
|
||||
import Provider from './common/Provider';
|
||||
import utils from '../utils';
|
||||
import workspaceSvc from '../workspaceSvc';
|
||||
import userSvc from '../userSvc';
|
||||
|
||||
const savedSha = {};
|
||||
|
||||
export default new Provider({
|
||||
id: 'gitee',
|
||||
name: 'Gitee',
|
||||
getToken({ sub }) {
|
||||
return store.getters['data/giteeTokensBySub'][sub];
|
||||
},
|
||||
getLocationUrl({
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
}) {
|
||||
return `https://gitee.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/tree/${encodeURIComponent(branch)}/${utils.encodeUrlPath(path)}`;
|
||||
},
|
||||
getLocationDescription({ path }) {
|
||||
return path;
|
||||
},
|
||||
async downloadContent(token, syncLocation) {
|
||||
const { sha, data } = await giteeHelper.downloadFile({
|
||||
...syncLocation,
|
||||
token,
|
||||
});
|
||||
savedSha[syncLocation.id] = sha;
|
||||
return Provider.parseContent(data, `${syncLocation.fileId}/content`);
|
||||
},
|
||||
async uploadContent(token, content, syncLocation) {
|
||||
if (!savedSha[syncLocation.id]) {
|
||||
try {
|
||||
// Get the last sha
|
||||
await this.downloadContent(token, syncLocation);
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
const sha = savedSha[syncLocation.id];
|
||||
delete savedSha[syncLocation.id];
|
||||
await giteeHelper.uploadFile({
|
||||
...syncLocation,
|
||||
token,
|
||||
content: Provider.serializeContent(content),
|
||||
sha,
|
||||
});
|
||||
return syncLocation;
|
||||
},
|
||||
async publish(token, html, metadata, publishLocation) {
|
||||
try {
|
||||
// Get the last sha
|
||||
await this.downloadContent(token, publishLocation);
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
const sha = savedSha[publishLocation.id];
|
||||
delete savedSha[publishLocation.id];
|
||||
await giteeHelper.uploadFile({
|
||||
...publishLocation,
|
||||
token,
|
||||
content: html,
|
||||
sha,
|
||||
});
|
||||
return publishLocation;
|
||||
},
|
||||
async openFile(token, syncLocation) {
|
||||
// Check if the file exists and open it
|
||||
if (!Provider.openFileWithLocation(syncLocation)) {
|
||||
// Download content from Gitee
|
||||
let content;
|
||||
try {
|
||||
content = await this.downloadContent(token, syncLocation);
|
||||
} catch (e) {
|
||||
store.dispatch('notification/error', `Could not open file ${syncLocation.path}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the file
|
||||
let name = syncLocation.path;
|
||||
const slashPos = name.lastIndexOf('/');
|
||||
if (slashPos > -1 && slashPos < name.length - 1) {
|
||||
name = name.slice(slashPos + 1);
|
||||
}
|
||||
const dotPos = name.lastIndexOf('.');
|
||||
if (dotPos > 0 && slashPos < name.length) {
|
||||
name = name.slice(0, dotPos);
|
||||
}
|
||||
const item = await workspaceSvc.createFile({
|
||||
name,
|
||||
parentId: store.getters['file/current'].parentId,
|
||||
text: content.text,
|
||||
properties: content.properties,
|
||||
discussions: content.discussions,
|
||||
comments: content.comments,
|
||||
}, true);
|
||||
store.commit('file/setCurrentId', item.id);
|
||||
workspaceSvc.addSyncLocation({
|
||||
...syncLocation,
|
||||
fileId: item.id,
|
||||
});
|
||||
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Gitee.`);
|
||||
}
|
||||
},
|
||||
makeLocation(token, owner, repo, branch, path) {
|
||||
return {
|
||||
providerId: this.id,
|
||||
sub: token.sub,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
};
|
||||
},
|
||||
async listFileRevisions({ token, syncLocation }) {
|
||||
const entries = await giteeHelper.getCommits({
|
||||
...syncLocation,
|
||||
token,
|
||||
});
|
||||
|
||||
return entries.map(({
|
||||
author,
|
||||
committer,
|
||||
commit,
|
||||
sha,
|
||||
}) => {
|
||||
let user;
|
||||
if (author && author.login) {
|
||||
user = author;
|
||||
} else if (committer && committer.login) {
|
||||
user = committer;
|
||||
}
|
||||
const sub = `${giteeHelper.subPrefix}:${user.login}`;
|
||||
userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
||||
const date = (commit.author && commit.author.date)
|
||||
|| (commit.committer && commit.committer.date);
|
||||
return {
|
||||
id: sha,
|
||||
sub,
|
||||
created: date ? new Date(date).getTime() : 1,
|
||||
};
|
||||
});
|
||||
},
|
||||
async loadFileRevision() {
|
||||
// Revision are already loaded
|
||||
return false;
|
||||
},
|
||||
async getFileRevisionContent({
|
||||
token,
|
||||
contentId,
|
||||
syncLocation,
|
||||
revisionId,
|
||||
}) {
|
||||
const { data } = await giteeHelper.downloadFile({
|
||||
...syncLocation,
|
||||
token,
|
||||
branch: revisionId,
|
||||
});
|
||||
return Provider.parseContent(data, contentId);
|
||||
},
|
||||
});
|
281
src/services/providers/giteeWorkspaceProvider.js
Normal file
281
src/services/providers/giteeWorkspaceProvider.js
Normal file
@ -0,0 +1,281 @@
|
||||
import store from '../../store';
|
||||
import giteeHelper from './helpers/giteeHelper';
|
||||
import Provider from './common/Provider';
|
||||
import utils from '../utils';
|
||||
import userSvc from '../userSvc';
|
||||
import gitWorkspaceSvc from '../gitWorkspaceSvc';
|
||||
import badgeSvc from '../badgeSvc';
|
||||
|
||||
const getAbsolutePath = ({ id }) =>
|
||||
`${store.getters['workspace/currentWorkspace'].path || ''}${id}`;
|
||||
|
||||
export default new Provider({
|
||||
id: 'giteeWorkspace',
|
||||
name: 'Gitee',
|
||||
getToken() {
|
||||
return store.getters['workspace/syncToken'];
|
||||
},
|
||||
getWorkspaceParams({
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
}) {
|
||||
return {
|
||||
providerId: this.id,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
};
|
||||
},
|
||||
getWorkspaceLocationUrl({
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
}) {
|
||||
return `https://gitee.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/tree/${encodeURIComponent(branch)}/${utils.encodeUrlPath(path)}`;
|
||||
},
|
||||
getSyncDataUrl({ id }) {
|
||||
const { owner, repo, branch } = store.getters['workspace/currentWorkspace'];
|
||||
return `https://gitee.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/tree/${encodeURIComponent(branch)}/${utils.encodeUrlPath(getAbsolutePath({ id }))}`;
|
||||
},
|
||||
getSyncDataDescription({ id }) {
|
||||
return getAbsolutePath({ id });
|
||||
},
|
||||
async initWorkspace() {
|
||||
const { owner, repo, branch } = utils.queryParams;
|
||||
const workspaceParams = this.getWorkspaceParams({ owner, repo, branch });
|
||||
if (!branch) {
|
||||
workspaceParams.branch = 'master';
|
||||
}
|
||||
|
||||
// Extract path param
|
||||
const path = (utils.queryParams.path || '')
|
||||
.trim()
|
||||
.replace(/^\/*/, '') // Remove leading `/`
|
||||
.replace(/\/*$/, '/'); // Add trailing `/`
|
||||
if (path !== '/') {
|
||||
workspaceParams.path = path;
|
||||
}
|
||||
|
||||
const workspaceId = utils.makeWorkspaceId(workspaceParams);
|
||||
const workspace = store.getters['workspace/workspacesById'][workspaceId];
|
||||
|
||||
// See if we already have a token
|
||||
let token;
|
||||
if (workspace) {
|
||||
// Token sub is in the workspace
|
||||
token = store.getters['data/giteeTokensBySub'][workspace.sub];
|
||||
}
|
||||
if (!token) {
|
||||
await store.dispatch('modal/open', { type: 'giteeAccount' });
|
||||
token = await giteeHelper.addAccount();
|
||||
}
|
||||
|
||||
if (!workspace) {
|
||||
const pathEntries = (path || '').split('/');
|
||||
const name = pathEntries[pathEntries.length - 2] || repo; // path ends with `/`
|
||||
store.dispatch('workspace/patchWorkspacesById', {
|
||||
[workspaceId]: {
|
||||
...workspaceParams,
|
||||
id: workspaceId,
|
||||
sub: token.sub,
|
||||
name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
badgeSvc.addBadge('addGithubWorkspace');
|
||||
return store.getters['workspace/workspacesById'][workspaceId];
|
||||
},
|
||||
getChanges() {
|
||||
return giteeHelper.getTree({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token: this.getToken(),
|
||||
});
|
||||
},
|
||||
prepareChanges(tree) {
|
||||
return gitWorkspaceSvc.makeChanges(tree);
|
||||
},
|
||||
async saveWorkspaceItem({ item }) {
|
||||
const syncData = {
|
||||
id: store.getters.gitPathsByItemId[item.id],
|
||||
type: item.type,
|
||||
hash: item.hash,
|
||||
};
|
||||
|
||||
// Files and folders are not in git, only contents
|
||||
if (item.type === 'file' || item.type === 'folder') {
|
||||
return { syncData };
|
||||
}
|
||||
|
||||
// locations are stored as paths, so we upload an empty file
|
||||
const syncToken = store.getters['workspace/syncToken'];
|
||||
await giteeHelper.uploadFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token: syncToken,
|
||||
path: getAbsolutePath(syncData),
|
||||
content: '',
|
||||
sha: gitWorkspaceSvc.shaByPath[syncData.id],
|
||||
});
|
||||
|
||||
// Return sync data to save
|
||||
return { syncData };
|
||||
},
|
||||
async removeWorkspaceItem({ syncData }) {
|
||||
if (gitWorkspaceSvc.shaByPath[syncData.id]) {
|
||||
const syncToken = store.getters['workspace/syncToken'];
|
||||
await giteeHelper.removeFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token: syncToken,
|
||||
path: getAbsolutePath(syncData),
|
||||
sha: gitWorkspaceSvc.shaByPath[syncData.id],
|
||||
});
|
||||
}
|
||||
},
|
||||
async downloadWorkspaceContent({
|
||||
token,
|
||||
contentId,
|
||||
contentSyncData,
|
||||
fileSyncData,
|
||||
}) {
|
||||
const { sha, data } = await giteeHelper.downloadFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token,
|
||||
path: getAbsolutePath(fileSyncData),
|
||||
});
|
||||
gitWorkspaceSvc.shaByPath[fileSyncData.id] = sha;
|
||||
const content = Provider.parseContent(data, contentId);
|
||||
return {
|
||||
content,
|
||||
contentSyncData: {
|
||||
...contentSyncData,
|
||||
hash: content.hash,
|
||||
sha,
|
||||
},
|
||||
};
|
||||
},
|
||||
async downloadWorkspaceData({ token, syncData }) {
|
||||
if (!syncData) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { sha, data } = await giteeHelper.downloadFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token,
|
||||
path: getAbsolutePath(syncData),
|
||||
});
|
||||
gitWorkspaceSvc.shaByPath[syncData.id] = sha;
|
||||
const item = JSON.parse(data);
|
||||
return {
|
||||
item,
|
||||
syncData: {
|
||||
...syncData,
|
||||
hash: item.hash,
|
||||
sha,
|
||||
},
|
||||
};
|
||||
},
|
||||
async uploadWorkspaceContent({ token, content, file }) {
|
||||
const path = store.getters.gitPathsByItemId[file.id];
|
||||
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
|
||||
const res = await giteeHelper.uploadFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token,
|
||||
path: absolutePath,
|
||||
content: Provider.serializeContent(content),
|
||||
sha: gitWorkspaceSvc.shaByPath[path],
|
||||
});
|
||||
|
||||
// Return new sync data
|
||||
return {
|
||||
contentSyncData: {
|
||||
id: store.getters.gitPathsByItemId[content.id],
|
||||
type: content.type,
|
||||
hash: content.hash,
|
||||
sha: res.content.sha,
|
||||
},
|
||||
fileSyncData: {
|
||||
id: path,
|
||||
type: 'file',
|
||||
hash: file.hash,
|
||||
},
|
||||
};
|
||||
},
|
||||
async uploadWorkspaceData({ token, item }) {
|
||||
const path = store.getters.gitPathsByItemId[item.id];
|
||||
const syncData = {
|
||||
id: path,
|
||||
type: item.type,
|
||||
hash: item.hash,
|
||||
};
|
||||
const res = await giteeHelper.uploadFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token,
|
||||
path: getAbsolutePath(syncData),
|
||||
content: JSON.stringify(item),
|
||||
sha: gitWorkspaceSvc.shaByPath[path],
|
||||
});
|
||||
|
||||
return {
|
||||
syncData: {
|
||||
...syncData,
|
||||
sha: res.content.sha,
|
||||
},
|
||||
};
|
||||
},
|
||||
async listFileRevisions({ token, fileSyncDataId }) {
|
||||
const { owner, repo, branch } = store.getters['workspace/currentWorkspace'];
|
||||
const entries = await giteeHelper.getCommits({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
sha: branch,
|
||||
path: getAbsolutePath({ id: fileSyncDataId }),
|
||||
});
|
||||
|
||||
return entries.map(({
|
||||
author,
|
||||
committer,
|
||||
commit,
|
||||
sha,
|
||||
}) => {
|
||||
let user;
|
||||
if (author && author.login) {
|
||||
user = author;
|
||||
} else if (committer && committer.login) {
|
||||
user = committer;
|
||||
}
|
||||
const sub = `${giteeHelper.subPrefix}:${user.login}`;
|
||||
userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
||||
const date = (commit.author && commit.author.date)
|
||||
|| (commit.committer && commit.committer.date)
|
||||
|| 1;
|
||||
return {
|
||||
id: sha,
|
||||
sub,
|
||||
created: new Date(date).getTime(),
|
||||
};
|
||||
});
|
||||
},
|
||||
async loadFileRevision() {
|
||||
// Revisions are already loaded
|
||||
return false;
|
||||
},
|
||||
async getFileRevisionContent({
|
||||
token,
|
||||
contentId,
|
||||
fileSyncDataId,
|
||||
revisionId,
|
||||
}) {
|
||||
const { data } = await giteeHelper.downloadFile({
|
||||
...store.getters['workspace/currentWorkspace'],
|
||||
token,
|
||||
branch: revisionId,
|
||||
path: getAbsolutePath({ id: fileSyncDataId }),
|
||||
});
|
||||
return Provider.parseContent(data, contentId);
|
||||
},
|
||||
});
|
315
src/services/providers/helpers/giteeHelper.js
Normal file
315
src/services/providers/helpers/giteeHelper.js
Normal file
@ -0,0 +1,315 @@
|
||||
import utils from '../../utils';
|
||||
import networkSvc from '../../networkSvc';
|
||||
import store from '../../../store';
|
||||
import userSvc from '../../userSvc';
|
||||
import badgeSvc from '../../badgeSvc';
|
||||
|
||||
const request = (token, options) => networkSvc.request({
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers || {},
|
||||
Authorization: `token ${token.accessToken}`,
|
||||
},
|
||||
params: {
|
||||
...options.params || {},
|
||||
t: Date.now(), // Prevent from caching
|
||||
},
|
||||
});
|
||||
|
||||
const repoRequest = (token, owner, repo, options) => request(token, {
|
||||
...options,
|
||||
url: `https://gitee.com/api/v5/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${options.url}`,
|
||||
})
|
||||
.then(res => res.body);
|
||||
|
||||
const getCommitMessage = (name, path) => {
|
||||
const message = store.getters['data/computedSettings'].git[name];
|
||||
return message.replace(/{{path}}/g, path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Getting a user from its userId is not feasible with API v3.
|
||||
* Using an undocumented endpoint...
|
||||
*/
|
||||
const subPrefix = 'ge';
|
||||
userSvc.setInfoResolver('gitee', subPrefix, async (sub) => {
|
||||
try {
|
||||
const user = (await networkSvc.request({
|
||||
url: `https://gitee.com/api/v5/users/${sub}`,
|
||||
params: {
|
||||
t: Date.now(), // Prevent from caching
|
||||
},
|
||||
})).body;
|
||||
|
||||
return {
|
||||
id: `${subPrefix}:${user.login}`,
|
||||
name: user.login,
|
||||
imageUrl: user.avatar_url || '',
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.status !== 404) {
|
||||
throw new Error('RETRY');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
subPrefix,
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/apps/building-oauth-apps/authorization-options-for-oauth-apps/
|
||||
*/
|
||||
async startOauth2(scopes, sub = null, silent = false) {
|
||||
const clientId = store.getters['data/serverConf'].giteeClientId;
|
||||
|
||||
// Get an OAuth2 code
|
||||
const { code } = await networkSvc.startOauth2(
|
||||
'https://gitee.com/oauth/authorize',
|
||||
{
|
||||
client_id: clientId,
|
||||
scope: 'projects pull_requests',
|
||||
response_type: 'code',
|
||||
},
|
||||
silent,
|
||||
);
|
||||
|
||||
// Exchange code with token
|
||||
const accessToken = (await networkSvc.request({
|
||||
method: 'GET',
|
||||
url: 'oauth2/giteeToken',
|
||||
params: {
|
||||
clientId,
|
||||
code,
|
||||
},
|
||||
})).body;
|
||||
|
||||
// Call the user info endpoint
|
||||
const user = (await networkSvc.request({
|
||||
method: 'GET',
|
||||
url: 'https://gitee.com/api/v5/user',
|
||||
params: {
|
||||
access_token: accessToken,
|
||||
},
|
||||
})).body;
|
||||
userSvc.addUserInfo({
|
||||
id: `${subPrefix}:${user.login}`,
|
||||
name: user.login,
|
||||
imageUrl: user.avatar_url || '',
|
||||
});
|
||||
|
||||
// Check the returned sub consistency
|
||||
if (sub && `${user.login}` !== sub) {
|
||||
throw new Error('Gitee account ID not expected.');
|
||||
}
|
||||
|
||||
// Build token object including scopes and sub
|
||||
const token = {
|
||||
scopes,
|
||||
accessToken,
|
||||
name: user.login,
|
||||
sub: `${user.login}`,
|
||||
};
|
||||
|
||||
// Add token to gitee tokens
|
||||
store.dispatch('data/addGiteeToken', token);
|
||||
return token;
|
||||
},
|
||||
async addAccount() {
|
||||
const token = await this.startOauth2();
|
||||
badgeSvc.addBadge('addGiteeAccount');
|
||||
return token;
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/repos/commits/#get-a-single-commit
|
||||
* https://developer.gitee.com/v3/git/trees/#get-a-tree
|
||||
*/
|
||||
async getTree({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
}) {
|
||||
const { commit } = await repoRequest(token, owner, repo, {
|
||||
url: `commits/${encodeURIComponent(branch)}`,
|
||||
});
|
||||
const { tree, truncated } = await repoRequest(token, owner, repo, {
|
||||
url: `git/trees/${encodeURIComponent(commit.tree.sha)}?recursive=1`,
|
||||
});
|
||||
if (truncated) {
|
||||
throw new Error('Git tree too big. Please remove some files in the repository.');
|
||||
}
|
||||
return tree;
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/repos/commits/#list-commits-on-a-repository
|
||||
*/
|
||||
async getCommits({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
path,
|
||||
}) {
|
||||
return repoRequest(token, owner, repo, {
|
||||
url: 'commits',
|
||||
params: { sha, path },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/repos/contents/#create-a-file
|
||||
* https://developer.gitee.com/v3/repos/contents/#update-a-file
|
||||
*/
|
||||
async uploadFile({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
content,
|
||||
sha,
|
||||
}) {
|
||||
return repoRequest(token, owner, repo, {
|
||||
method: sha ? 'PUT' : 'POST',
|
||||
url: `contents/${encodeURIComponent(path)}`,
|
||||
body: {
|
||||
message: getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
||||
content: utils.encodeBase64(content),
|
||||
sha,
|
||||
branch,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/repos/contents/#delete-a-file
|
||||
*/
|
||||
async removeFile({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
sha,
|
||||
}) {
|
||||
return repoRequest(token, owner, repo, {
|
||||
method: 'DELETE',
|
||||
url: `contents/${encodeURIComponent(path)}`,
|
||||
body: {
|
||||
message: getCommitMessage('deleteFileMessage', path),
|
||||
sha,
|
||||
branch,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/repos/contents/#get-contents
|
||||
*/
|
||||
async downloadFile({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
path,
|
||||
}) {
|
||||
const { sha, content } = await repoRequest(token, owner, repo, {
|
||||
url: `contents/${encodeURIComponent(path)}`,
|
||||
params: { ref: branch },
|
||||
});
|
||||
return {
|
||||
sha,
|
||||
data: utils.decodeBase64(content),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/gists/#create-a-gist
|
||||
* https://developer.gitee.com/v3/gists/#edit-a-gist
|
||||
*/
|
||||
async uploadGist({
|
||||
token,
|
||||
description,
|
||||
filename,
|
||||
content,
|
||||
isPublic,
|
||||
gistId,
|
||||
}) {
|
||||
const { body } = await request(token, gistId ? {
|
||||
method: 'PATCH',
|
||||
url: `https://gitee.com/api/v5/gists/${gistId}`,
|
||||
body: {
|
||||
description,
|
||||
files: {
|
||||
[filename]: {
|
||||
content,
|
||||
},
|
||||
},
|
||||
},
|
||||
} : {
|
||||
method: 'POST',
|
||||
url: 'https://gitee.com/api/v5/gists',
|
||||
body: {
|
||||
description,
|
||||
files: {
|
||||
[filename]: {
|
||||
content,
|
||||
},
|
||||
},
|
||||
public: isPublic,
|
||||
},
|
||||
});
|
||||
return body;
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/gists/#get-a-single-gist
|
||||
*/
|
||||
async downloadGist({
|
||||
token,
|
||||
gistId,
|
||||
filename,
|
||||
}) {
|
||||
const result = (await request(token, {
|
||||
url: `https://gitee.com/api/v5/gists/${gistId}`,
|
||||
})).body.files[filename];
|
||||
if (!result) {
|
||||
throw new Error('Gist file not found.');
|
||||
}
|
||||
return result.content;
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/gists/#list-gist-commits
|
||||
*/
|
||||
async getGistCommits({
|
||||
token,
|
||||
gistId,
|
||||
}) {
|
||||
const { body } = await request(token, {
|
||||
url: `https://gitee.com/api/v5/gists/${gistId}/commits`,
|
||||
});
|
||||
return body;
|
||||
},
|
||||
|
||||
/**
|
||||
* https://developer.gitee.com/v3/gists/#get-a-specific-revision-of-a-gist
|
||||
*/
|
||||
async downloadGistRevision({
|
||||
token,
|
||||
gistId,
|
||||
filename,
|
||||
sha,
|
||||
}) {
|
||||
const result = (await request(token, {
|
||||
url: `https://gitee.com/api/v5/gists/${gistId}/${sha}`,
|
||||
})).body.files[filename];
|
||||
if (!result) {
|
||||
throw new Error('Gist file not found.');
|
||||
}
|
||||
return result.content;
|
||||
},
|
||||
};
|
@ -89,8 +89,8 @@ export default {
|
||||
const user = (await networkSvc.request({
|
||||
method: 'GET',
|
||||
url: 'https://api.github.com/user',
|
||||
params: {
|
||||
access_token: accessToken,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})).body;
|
||||
userSvc.addUserInfo({
|
||||
|
@ -625,7 +625,7 @@ export default {
|
||||
async openPicker(token, type = 'doc') {
|
||||
const scopes = type === 'img' ? photosScopes : getDriveScopes(token);
|
||||
if (!window.google) {
|
||||
await networkSvc.loadScript('https://apis.google.com/js/api.js');
|
||||
await networkSvc.loadScript('https://www.gstatic.cn/charts/loader.js');
|
||||
await new Promise((resolve, reject) => window.gapi.load('picker', {
|
||||
callback: resolve,
|
||||
onerror: reject,
|
||||
|
@ -7,6 +7,7 @@ import providerRegistry from './providers/common/providerRegistry';
|
||||
import googleDriveAppDataProvider from './providers/googleDriveAppDataProvider';
|
||||
import './providers/couchdbWorkspaceProvider';
|
||||
import './providers/githubWorkspaceProvider';
|
||||
import './providers/giteeWorkspaceProvider';
|
||||
import './providers/gitlabWorkspaceProvider';
|
||||
import './providers/googleDriveWorkspaceProvider';
|
||||
import tempFileSvc from './tempFileSvc';
|
||||
|
@ -211,6 +211,7 @@ export default {
|
||||
couchdbTokensBySub: (state, { tokensByType }) => tokensByType.couchdb || {},
|
||||
dropboxTokensBySub: (state, { tokensByType }) => tokensByType.dropbox || {},
|
||||
githubTokensBySub: (state, { tokensByType }) => tokensByType.github || {},
|
||||
giteeTokensBySub: (state, { tokensByType }) => tokensByType.gitee || {},
|
||||
gitlabTokensBySub: (state, { tokensByType }) => tokensByType.gitlab || {},
|
||||
wordpressTokensBySub: (state, { tokensByType }) => tokensByType.wordpress || {},
|
||||
zendeskTokensBySub: (state, { tokensByType }) => tokensByType.zendesk || {},
|
||||
@ -303,6 +304,7 @@ export default {
|
||||
addCouchdbToken: tokenAdder('couchdb'),
|
||||
addDropboxToken: tokenAdder('dropbox'),
|
||||
addGithubToken: tokenAdder('github'),
|
||||
addGiteeToken: tokenAdder('gitee'),
|
||||
addGitlabToken: tokenAdder('gitlab'),
|
||||
addWordpressToken: tokenAdder('wordpress'),
|
||||
addZendeskToken: tokenAdder('zendesk'),
|
||||
|
@ -44,9 +44,11 @@ export default {
|
||||
workspacesById[currentWorkspaceId] || mainWorkspace,
|
||||
currentWorkspaceIsGit: (state, { currentWorkspace }) =>
|
||||
currentWorkspace.providerId === 'githubWorkspace'
|
||||
|| currentWorkspace.providerId === 'giteeWorkspace'
|
||||
|| currentWorkspace.providerId === 'gitlabWorkspace',
|
||||
currentWorkspaceHasUniquePaths: (state, { currentWorkspace }) =>
|
||||
currentWorkspace.providerId === 'githubWorkspace'
|
||||
|| currentWorkspace.providerId === 'giteeWorkspace'
|
||||
|| currentWorkspace.providerId === 'gitlabWorkspace',
|
||||
lastSyncActivityKey: (state, { currentWorkspace }) => `${currentWorkspace.id}/lastSyncActivity`,
|
||||
lastFocusKey: (state, { currentWorkspace }) => `${currentWorkspace.id}/lastWindowFocus`,
|
||||
@ -63,6 +65,8 @@ export default {
|
||||
return rootGetters['data/googleTokensBySub'][currentWorkspace.sub];
|
||||
case 'githubWorkspace':
|
||||
return rootGetters['data/githubTokensBySub'][currentWorkspace.sub];
|
||||
case 'giteeWorkspace':
|
||||
return rootGetters['data/giteeTokensBySub'][currentWorkspace.sub];
|
||||
case 'gitlabWorkspace':
|
||||
return rootGetters['data/gitlabTokensBySub'][currentWorkspace.sub];
|
||||
case 'couchdbWorkspace':
|
||||
@ -78,6 +82,8 @@ export default {
|
||||
return 'google';
|
||||
case 'githubWorkspace':
|
||||
return 'github';
|
||||
case 'giteeWorkspace':
|
||||
return 'gitee';
|
||||
case 'gitlabWorkspace':
|
||||
return 'gitlab';
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<title>StackEdit – In-browser Markdown editor</title>
|
||||
<link rel="canonical" href="https://stackedit.io/">
|
||||
<link rel="canonical" href="https://edit.qicoder.com/">
|
||||
<link rel="icon" href="static/landing/favicon.ico" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="static/landing/favicon.ico" type="image/x-icon">
|
||||
<meta charset="UTF-8">
|
||||
@ -13,7 +13,7 @@
|
||||
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612">
|
||||
<meta name="google-site-verification" content="iWDn0T2r2_bDQWp_nW23MGePbO9X0M8wQSzbOU70pFQ" />
|
||||
<link rel="stylesheet" href="https://stackedit.io/style.css">
|
||||
<link rel="stylesheet" href="https://edit.qicoder.com/style.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: #fbfbfb;
|
||||
|
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://stackedit.io/</loc>
|
||||
<loc>https://edit.qicoder.com/</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://stackedit.io/app</loc>
|
||||
<loc>https://edit.qicoder.com/app</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@ -16,7 +16,7 @@
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://stackedit.io/privacy_policy.html</loc>
|
||||
<loc>https://edit.qicoder.com/privacy_policy.html</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
|
Loading…
Reference in New Issue
Block a user