gitee gitea 支持bugfix

This commit is contained in:
xiaoqi.cxq 2022-06-01 17:37:31 +08:00
parent cb58d05147
commit 198c8cb647
11 changed files with 156 additions and 67 deletions

View File

@ -8,5 +8,5 @@ module.exports = merge(prodEnv, {
GITHUB_CLIENT_SECRET: '"80df676597abded1450926861965cc3f9bead6a0"', GITHUB_CLIENT_SECRET: '"80df676597abded1450926861965cc3f9bead6a0"',
GITEE_CLIENT_ID: '"925ba7c78b85dec984f7877e4aca5cab10ae333c6d68e761bdb0b9dfb8f55672"', GITEE_CLIENT_ID: '"925ba7c78b85dec984f7877e4aca5cab10ae333c6d68e761bdb0b9dfb8f55672"',
GITEE_CLIENT_SECRET: '"f05731066e42d307339dc8ebbb037a103881dafc7207a359a393b87749f1c562"', GITEE_CLIENT_SECRET: '"f05731066e42d307339dc8ebbb037a103881dafc7207a359a393b87749f1c562"',
GITEE_CALLBACK: '"http://test.local.com/oauth2/callback"' CLIENT_ID: '"thF3qCGLN39OtafjGnqHyj6n02WwE6xD"',
}) })

View File

@ -10,7 +10,6 @@ const githubClientId = process.env.GITHUB_CLIENT_ID;
const githubClientSecret = process.env.GITHUB_CLIENT_SECRET; const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
const giteeClientId = process.env.GITEE_CLIENT_ID; const giteeClientId = process.env.GITEE_CLIENT_ID;
const giteeClientSecret = process.env.GITEE_CLIENT_SECRET; const giteeClientSecret = process.env.GITEE_CLIENT_SECRET;
const giteeCallback = process.env.GITEE_CALLBACK;
const googleClientId = process.env.GOOGLE_CLIENT_ID; const googleClientId = process.env.GOOGLE_CLIENT_ID;
const googleApiKey = process.env.GOOGLE_API_KEY; const googleApiKey = process.env.GOOGLE_API_KEY;
const wordpressClientId = process.env.WORDPRESS_CLIENT_ID; const wordpressClientId = process.env.WORDPRESS_CLIENT_ID;
@ -27,7 +26,6 @@ exports.values = {
githubClientSecret, githubClientSecret,
giteeClientId, giteeClientId,
giteeClientSecret, giteeClientSecret,
giteeCallback,
googleClientId, googleClientId,
googleApiKey, googleApiKey,
wordpressClientId, wordpressClientId,

View File

@ -9,17 +9,17 @@
</select> </select>
</p> </p>
<p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Google</a> 以同步您的主工作区</p> <p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Google</a> 以同步您的主工作区</p>
<p v-else-if="loading">Loading history</p> <p v-else-if="loading">历史版本加载中</p>
<p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> has no history.</p> <p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p>
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else> <div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else>
<div class="menu-entry__icon menu-entry__icon--image"> <div class="menu-entry__icon menu-entry__icon--image">
<icon-provider :provider-id="syncLocation.providerId"></icon-provider> <icon-provider :provider-id="syncLocation.providerId"></icon-provider>
</div> </div>
<span v-if="syncLocation.url"> <span v-if="syncLocation.url">
The following revisions are stored in <a :href="syncLocation.url" target="_blank">{{ syncLocationProviderName }}</a>. 下面的历史版本存储在 <a :href="syncLocation.url" target="_blank">{{ syncLocationProviderName }}</a>.
</span> </span>
<span v-else> <span v-else>
The following revisions are stored in {{ syncLocationProviderName }}. 下面的历史版本存储在 {{ syncLocationProviderName }}.
</span> </span>
</div> </div>
</div> </div>
@ -39,7 +39,7 @@
</div> </div>
<div class="history__spacer history__spacer--last" v-if="revisions.length"></div> <div class="history__spacer history__spacer--last" v-if="revisions.length"></div>
<div class="flex flex--row flex--end" v-if="showMoreButton"> <div class="flex flex--row flex--end" v-if="showMoreButton">
<button class="history__button button" @click="showMore">More</button> <button class="history__button button" @click="showMore">更多</button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -135,17 +135,24 @@ export default new Provider({
token, token,
}); });
return entries.map((entry) => { return entries.map(({
const email = entry.author_email || entry.committer_email; author,
const sub = `${giteaHelper.subPrefix}:${token.serverUrl}/${email}`; committer,
userSvc.addUserInfo({ commit,
id: sub, sha,
name: entry.author_name || entry.committer_name, }) => {
imageUrl: '', let user;
}); if (author && author.login) {
const date = entry.authored_date || entry.committed_date || 1; user = author;
} else if (committer && committer.login) {
user = committer;
}
const sub = `${giteaHelper.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 { return {
id: entry.id, id: sha,
sub, sub,
created: date ? new Date(date).getTime() : 1, created: date ? new Date(date).getTime() : 1,
}; };

View File

@ -251,19 +251,27 @@ export default new Provider({
path: getAbsolutePath({ id: fileSyncDataId }), path: getAbsolutePath({ id: fileSyncDataId }),
}); });
return entries.map((entry) => { return entries.map(({
const email = entry.author_email || entry.committer_email; author,
const sub = `${giteaHelper.subPrefix}:${token.serverUrl}/${email}`; committer,
userSvc.addUserInfo({ commit,
id: sub, sha,
name: entry.author_name || entry.committer_name, }) => {
imageUrl: '', // No way to get user's avatar url... let user;
}); if (author && author.login) {
const date = entry.authored_date || entry.committed_date || 1; user = author;
} else if (committer && committer.login) {
user = committer;
}
const sub = `${giteaHelper.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 { return {
id: entry.id, id: sha,
sub, sub,
created: date ? new Date(date).getTime() : 1, created: new Date(date).getTime(),
}; };
}); });
}, },

View File

@ -135,6 +135,9 @@ export default new Provider({
user = committer; user = committer;
} }
const sub = `${giteeHelper.subPrefix}:${user.login}`; const sub = `${giteeHelper.subPrefix}:${user.login}`;
if (user.avatar_url && user.avatar_url.endsWith('.png')) {
user.avatar_url = `${user.avatar_url}!avatar60`;
}
userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url }); userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
const date = (commit.author && commit.author.date) const date = (commit.author && commit.author.date)
|| (commit.committer && commit.committer.date); || (commit.committer && commit.committer.date);

View File

@ -249,6 +249,9 @@ export default new Provider({
user = committer; user = committer;
} }
const sub = `${giteeHelper.subPrefix}:${user.login}`; const sub = `${giteeHelper.subPrefix}:${user.login}`;
if (user.avatar_url && user.avatar_url.endsWith('.png')) {
user.avatar_url = `${user.avatar_url}!avatar60`;
}
userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url }); userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
const date = (commit.author && commit.author.date) const date = (commit.author && commit.author.date)
|| (commit.committer && commit.committer.date) || (commit.committer && commit.committer.date)

View File

@ -5,6 +5,8 @@ import userSvc from '../../userSvc';
import badgeSvc from '../../badgeSvc'; import badgeSvc from '../../badgeSvc';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
const tokenExpirationMargin = 5 * 60 * 1000;
const request = ({ accessToken, serverUrl }, options) => networkSvc.request({ const request = ({ accessToken, serverUrl }, options) => networkSvc.request({
...options, ...options,
url: `${serverUrl}/api/v1/${options.url}`, url: `${serverUrl}/api/v1/${options.url}`,
@ -51,7 +53,12 @@ export default {
/** /**
* https://docs.gitea.io/en-us/oauth2-provider/ * https://docs.gitea.io/en-us/oauth2-provider/
*/ */
async startOauth2(serverUrl, applicationId, applicationSecret, sub = null, silent = false) { async startOauth2(
serverUrl, applicationId, applicationSecret,
sub = null, silent = false, refreshToken,
) {
let tokenBody;
if (!silent) {
// Get an OAuth2 code // Get an OAuth2 code
const { code } = await networkSvc.startOauth2( const { code } = await networkSvc.startOauth2(
`${serverUrl}/login/oauth/authorize`, `${serverUrl}/login/oauth/authorize`,
@ -62,9 +69,8 @@ export default {
}, },
silent, silent,
); );
// Exchange code with token // Exchange code with token
const accessToken = (await networkSvc.request({ tokenBody = (await networkSvc.request({
method: 'POST', method: 'POST',
url: `${serverUrl}/login/oauth/access_token`, url: `${serverUrl}/login/oauth/access_token`,
body: { body: {
@ -74,7 +80,23 @@ export default {
grant_type: 'authorization_code', grant_type: 'authorization_code',
redirect_uri: constants.oauth2RedirectUri, redirect_uri: constants.oauth2RedirectUri,
}, },
})).body.access_token; })).body;
} else {
// Exchange refreshToken with token
tokenBody = (await networkSvc.request({
method: 'POST',
url: `${serverUrl}/login/oauth/access_token`,
body: {
client_id: applicationId,
client_secret: applicationSecret,
refresh_token: refreshToken,
grant_type: 'refresh_token',
redirect_uri: constants.oauth2RedirectUri,
},
})).body;
}
const accessToken = tokenBody.access_token;
// Call the user info endpoint // Call the user info endpoint
const user = await request({ accessToken, serverUrl }, { const user = await request({ accessToken, serverUrl }, {
@ -96,6 +118,8 @@ export default {
const token = { const token = {
accessToken, accessToken,
name: user.username, name: user.username,
refreshToken: tokenBody.refresh_token,
expiresOn: Date.now() + (tokenBody.expires_in * 1000),
serverUrl, serverUrl,
sub: uniqueSub, sub: uniqueSub,
}; };
@ -104,6 +128,47 @@ export default {
store.dispatch('data/addGiteaToken', token); store.dispatch('data/addGiteaToken', token);
return token; return token;
}, },
// 刷新token
async refreshToken(token) {
const {
serverUrl,
applicationId,
applicationSecret,
sub,
} = token;
const lastToken = store.getters['data/giteaTokensBySub'][sub];
// 兼容旧的没有过期时间
if (!lastToken.expiresOn) {
await store.dispatch('modal/open', {
type: 'providerRedirection',
name: 'Gitea',
});
return this.startOauth2(serverUrl, applicationId, applicationSecret, sub);
}
// lastToken is not expired
if (lastToken.expiresOn > Date.now() + tokenExpirationMargin) {
return lastToken;
}
// existing token is about to expire.
// Try to get a new token in background
try {
return await this.startOauth2(
serverUrl, applicationId, applicationSecret,
sub, true, lastToken.refreshToken,
);
} catch (err) {
// If it fails try to popup a window
if (store.state.offline) {
throw err;
}
await store.dispatch('modal/open', {
type: 'providerRedirection',
name: 'Gitea',
});
return this.startOauth2(serverUrl, applicationId, applicationSecret, sub);
}
},
async addAccount(serverUrl, applicationId, applicationSecret, sub = null) { async addAccount(serverUrl, applicationId, applicationSecret, sub = null) {
const token = await this.startOauth2(serverUrl, applicationId, applicationSecret, sub); const token = await this.startOauth2(serverUrl, applicationId, applicationSecret, sub);
badgeSvc.addBadge('addGiteaAccount'); badgeSvc.addBadge('addGiteaAccount');
@ -129,7 +194,8 @@ export default {
projectId, projectId,
branch, branch,
}) { }) {
return request(token, { const refreshedToken = await this.refreshToken(token);
return request(refreshedToken, {
url: `repos/${projectId}/git/trees/${branch}`, url: `repos/${projectId}/git/trees/${branch}`,
params: { params: {
recursive: true, recursive: true,
@ -147,7 +213,8 @@ export default {
branch, branch,
path, path,
}) { }) {
return request(token, { const refreshedToken = await this.refreshToken(token);
return request(refreshedToken, {
url: `repos/${projectId}/commits`, url: `repos/${projectId}/commits`,
params: { params: {
sha: branch, sha: branch,
@ -168,7 +235,8 @@ export default {
content, content,
sha, sha,
}) { }) {
return request(token, { const refreshedToken = await this.refreshToken(token);
return request(refreshedToken, {
method: sha ? 'PUT' : 'POST', method: sha ? 'PUT' : 'POST',
url: `repos/${projectId}/contents/${encodeURIComponent(path)}`, url: `repos/${projectId}/contents/${encodeURIComponent(path)}`,
body: { body: {
@ -190,7 +258,8 @@ export default {
path, path,
sha, sha,
}) { }) {
return request(token, { const refreshedToken = await this.refreshToken(token);
return request(refreshedToken, {
method: 'DELETE', method: 'DELETE',
url: `repos/${projectId}/contents/${encodeURIComponent(path)}`, url: `repos/${projectId}/contents/${encodeURIComponent(path)}`,
body: { body: {
@ -210,7 +279,8 @@ export default {
branch, branch,
path, path,
}) { }) {
const { sha, content } = await request(token, { const refreshedToken = await this.refreshToken(token);
const { sha, content } = await request(refreshedToken, {
url: `repos/${projectId}/contents/${encodeURIComponent(path)}`, url: `repos/${projectId}/contents/${encodeURIComponent(path)}`,
params: { ref: branch }, params: { ref: branch },
}); });

View File

@ -139,7 +139,7 @@ export default {
return this.startOauth2(); return this.startOauth2();
} }
// lastToken is not expired // lastToken is not expired
if (lastToken.expiresOn > Date.now() - tokenExpirationMargin) { if (lastToken.expiresOn > Date.now() + tokenExpirationMargin) {
return lastToken; return lastToken;
} }

View File

@ -138,21 +138,21 @@ class RelativeTime {
const hr = Math.round(min / 60); const hr = Math.round(min / 60);
const day = Math.round(hr / 24); const day = Math.round(hr / 24);
if (ms < 0) { if (ms < 0) {
return 'just now'; return '刚刚';
} else if (sec < 45) { } else if (sec < 45) {
return 'just now'; return '刚刚';
} else if (sec < 90) { } else if (sec < 90) {
return 'a minute ago'; return '1分钟前';
} else if (min < 45) { } else if (min < 45) {
return `${min} minutes ago`; return `${min}分钟前`;
} else if (min < 90) { } else if (min < 90) {
return 'an hour ago'; return '1小时前';
} else if (hr < 24) { } else if (hr < 24) {
return `${hr} hours ago`; return `${hr}小时前`;
} else if (hr < 36) { } else if (hr < 36) {
return 'a day ago'; return '1天前';
} else if (day < 30) { } else if (day < 30) {
return `${day} days ago`; return `${day}天前`;
} }
return null; return null;
} }