支持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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/benweet/stackedit) [](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,
 | 
			
		||||
 | 
			
		||||
@ -3,4 +3,4 @@ var prodEnv = require('./prod.env')
 | 
			
		||||
 | 
			
		||||
module.exports = merge(prodEnv, {
 | 
			
		||||
  NODE_ENV: '"development"'
 | 
			
		||||
})
 | 
			
		||||
})
 | 
			
		||||
@ -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