From 35fdafde775327587911b055cc28e35700ea48df Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Tue, 12 Nov 2024 20:43:58 +0800 Subject: [PATCH 1/8] feat: optimize github token process Signed-off-by: frank-zsy --- src/manifest.json | 2 +- src/pages/Options/components/GitHubToken.tsx | 51 ++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index f69f83e0..ffe9b33f 100755 --- a/src/manifest.json +++ b/src/manifest.json @@ -31,7 +31,7 @@ "matches": [""] } ], - "permissions": ["storage"], + "permissions": ["storage", "identity"], "host_permissions": [""], "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self';" diff --git a/src/pages/Options/components/GitHubToken.tsx b/src/pages/Options/components/GitHubToken.tsx index 183d90a7..5701d514 100644 --- a/src/pages/Options/components/GitHubToken.tsx +++ b/src/pages/Options/components/GitHubToken.tsx @@ -20,6 +20,54 @@ const GitHubToken = () => { fetchToken(); }, []); + const handleOAuthToken = async () => { + const clientId = 'Ov23liyofMsuQYwtfGLb'; + const redirectUri = chrome.identity.getRedirectURL(); + const scope = encodeURIComponent('read:user'); + const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=token`; + + console.log('[FastPR]: Start authorization...'); + chrome.identity.launchWebAuthFlow( + { + url: authUrl, + interactive: true, + }, + function (redirectUrl) { + if (chrome.runtime.lastError || !redirectUrl) { + console.error(chrome.runtime.lastError ? chrome.runtime.lastError.message : 'Authorization failed.'); + return; + } + const code = new URL(redirectUrl).searchParams.get('code'); + console.log('[FastPR]: Get session code:', code?.slice(0, 2).padEnd(code.length - 2, '*')); + fetch('http://8.147.129.123/github', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ code }), + }).then(async (response) => { + if (!response.ok) { + console.error(`HTTP error! status: ${response.status}`); + } + const respData = await response.json(); + const token = respData.access_token; + + const userDataReq = await fetch(`https://api.github.com/user`, { + headers: { Authorization: `Bearer ${token}` }, + }); + + const userData = await userDataReq.json(); + if (userData === null || userData.message) { + showMessage(t('github_token_error_invalid'), 'error'); + } else { + console.log(`[FastPR]: Welcome: ${userData.login}`); + } + }); + } + ); + }; + const handleSave = () => { if (!token.trim()) { showMessage(t('github_token_error_empty'), 'error'); @@ -128,6 +176,9 @@ const GitHubToken = () => { + ); From d152f39282b4da74249278b0f2ee06ac513be6ba Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Tue, 12 Nov 2024 22:10:22 +0800 Subject: [PATCH 2/8] feat: add gitee token process Signed-off-by: frank-zsy --- src/pages/Options/components/GiteeToken.tsx | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/pages/Options/components/GiteeToken.tsx b/src/pages/Options/components/GiteeToken.tsx index c684c8ee..9edf448a 100644 --- a/src/pages/Options/components/GiteeToken.tsx +++ b/src/pages/Options/components/GiteeToken.tsx @@ -20,6 +20,56 @@ const GiteeToken = () => { fetchToken(); }, []); + const handleOAuthToken = async () => { + const clientId = 'e76727820aa539f3a59399d0bc48156df2057e81774617e433eeb49d1dad97b3'; + const redirectUri = chrome.identity.getRedirectURL(); + const scope = encodeURIComponent('user_info'); + const authUrl = `https://gitee.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code`; + + console.log('[FastPR]: Start authorization...'); + chrome.identity.launchWebAuthFlow( + { + url: authUrl, + interactive: true, + }, + function (redirectUrl) { + if (chrome.runtime.lastError || !redirectUrl) { + console.error(chrome.runtime.lastError ? chrome.runtime.lastError.message : 'Authorization failed.'); + return; + } + const code = new URL(redirectUrl).searchParams.get('code'); + console.log('[FastPR]: Get session code:', code?.slice(0, 2).padEnd(code.length - 2, '*')); + fetch('http://8.147.129.123/gitee', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ code }), + }).then(async (response) => { + if (!response.ok) { + console.error(`HTTP error! status: ${response.status}`); + } + const respData = await response.json(); + console.log(respData); + const token = respData.access_token; + + const userDataReq = await fetch(`https://gitee.com/api/v5/user`, { + headers: { Authorization: `Bearer ${token}` }, + }); + + const userData = await userDataReq.json(); + + if (userData === null || userData.message) { + showMessage(t('gitee_token_error_invalid'), 'error'); + } else { + console.log(`[FastPR]: Welcome: ${userData.login}`); + } + }); + } + ); + }; + const handleSave = () => { if (!token.trim()) { showMessage(t('gitee_token_error_empty'), 'error'); @@ -125,6 +175,9 @@ const GiteeToken = () => { + ); From ed52bb0f707614308c8e470bd6594f8e1bf37cfe Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Wed, 13 Nov 2024 15:28:45 +0800 Subject: [PATCH 3/8] fix: use callback to server directly Signed-off-by: frank-zsy --- src/pages/Options/components/GitHubToken.tsx | 48 ++++++++----------- src/pages/Options/components/GiteeToken.tsx | 49 ++++++++------------ 2 files changed, 38 insertions(+), 59 deletions(-) diff --git a/src/pages/Options/components/GitHubToken.tsx b/src/pages/Options/components/GitHubToken.tsx index 5701d514..33fde149 100644 --- a/src/pages/Options/components/GitHubToken.tsx +++ b/src/pages/Options/components/GitHubToken.tsx @@ -22,9 +22,10 @@ const GitHubToken = () => { const handleOAuthToken = async () => { const clientId = 'Ov23liyofMsuQYwtfGLb'; - const redirectUri = chrome.identity.getRedirectURL(); + const redirectUri = 'http://8.147.129.123/github'; + const callback = chrome.identity.getRedirectURL(); const scope = encodeURIComponent('read:user'); - const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=token`; + const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=token&state=${callback}`; console.log('[FastPR]: Start authorization...'); chrome.identity.launchWebAuthFlow( @@ -32,38 +33,27 @@ const GitHubToken = () => { url: authUrl, interactive: true, }, - function (redirectUrl) { + async (redirectUrl) => { if (chrome.runtime.lastError || !redirectUrl) { console.error(chrome.runtime.lastError ? chrome.runtime.lastError.message : 'Authorization failed.'); return; } - const code = new URL(redirectUrl).searchParams.get('code'); - console.log('[FastPR]: Get session code:', code?.slice(0, 2).padEnd(code.length - 2, '*')); - fetch('http://8.147.129.123/github', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - body: JSON.stringify({ code }), - }).then(async (response) => { - if (!response.ok) { - console.error(`HTTP error! status: ${response.status}`); - } - const respData = await response.json(); - const token = respData.access_token; - - const userDataReq = await fetch(`https://api.github.com/user`, { - headers: { Authorization: `Bearer ${token}` }, - }); - - const userData = await userDataReq.json(); - if (userData === null || userData.message) { - showMessage(t('github_token_error_invalid'), 'error'); - } else { - console.log(`[FastPR]: Welcome: ${userData.login}`); - } + const ret = new URL(redirectUrl).searchParams.get('ret'); + if (!ret) { + console.error('Ret not returned in callback URL, check the server config'); + return; + } + const retData = JSON.parse(decodeURIComponent(ret)); + const userDataReq = await fetch(`https://api.github.com/user`, { + headers: { Authorization: `Bearer ${retData.access_token}` }, }); + + const userData = await userDataReq.json(); + if (userData === null || userData.message) { + showMessage(t('github_token_error_invalid'), 'error'); + } else { + console.log(`[FastPR]: Welcome: ${userData.login}`); + } } ); }; diff --git a/src/pages/Options/components/GiteeToken.tsx b/src/pages/Options/components/GiteeToken.tsx index 9edf448a..e065a8c6 100644 --- a/src/pages/Options/components/GiteeToken.tsx +++ b/src/pages/Options/components/GiteeToken.tsx @@ -22,9 +22,10 @@ const GiteeToken = () => { const handleOAuthToken = async () => { const clientId = 'e76727820aa539f3a59399d0bc48156df2057e81774617e433eeb49d1dad97b3'; - const redirectUri = chrome.identity.getRedirectURL(); + const redirectUri = 'http://8.147.129.123/gitee'; const scope = encodeURIComponent('user_info'); - const authUrl = `https://gitee.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code`; + const callback = chrome.identity.getRedirectURL(); + const authUrl = `https://gitee.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${callback}`; console.log('[FastPR]: Start authorization...'); chrome.identity.launchWebAuthFlow( @@ -32,40 +33,28 @@ const GiteeToken = () => { url: authUrl, interactive: true, }, - function (redirectUrl) { + async function (redirectUrl) { if (chrome.runtime.lastError || !redirectUrl) { console.error(chrome.runtime.lastError ? chrome.runtime.lastError.message : 'Authorization failed.'); return; } - const code = new URL(redirectUrl).searchParams.get('code'); - console.log('[FastPR]: Get session code:', code?.slice(0, 2).padEnd(code.length - 2, '*')); - fetch('http://8.147.129.123/gitee', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - body: JSON.stringify({ code }), - }).then(async (response) => { - if (!response.ok) { - console.error(`HTTP error! status: ${response.status}`); - } - const respData = await response.json(); - console.log(respData); - const token = respData.access_token; - - const userDataReq = await fetch(`https://gitee.com/api/v5/user`, { - headers: { Authorization: `Bearer ${token}` }, - }); + const ret = new URL(redirectUrl).searchParams.get('ret'); + if (!ret) { + console.error('Ret not returned in callback URL, check the server config'); + return; + } + const retData = JSON.parse(decodeURIComponent(ret)); + const userDataReq = await fetch(`https://gitee.com/api/v5/user`, { + headers: { Authorization: `Bearer ${retData.access_token}` }, + }); - const userData = await userDataReq.json(); + const userData = await userDataReq.json(); - if (userData === null || userData.message) { - showMessage(t('gitee_token_error_invalid'), 'error'); - } else { - console.log(`[FastPR]: Welcome: ${userData.login}`); - } - }); + if (userData === null || userData.message) { + showMessage(t('gitee_token_error_invalid'), 'error'); + } else { + console.log(`[FastPR]: Welcome: ${userData.login}`); + } } ); }; From e35eb65432feb9fde3fc2539bf3b8ca8c2e467e6 Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Wed, 13 Nov 2024 15:37:33 +0800 Subject: [PATCH 4/8] fix: fix error message Signed-off-by: frank-zsy --- src/pages/Options/components/GitHubToken.tsx | 2 +- src/pages/Options/components/GiteeToken.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Options/components/GitHubToken.tsx b/src/pages/Options/components/GitHubToken.tsx index 33fde149..ea180211 100644 --- a/src/pages/Options/components/GitHubToken.tsx +++ b/src/pages/Options/components/GitHubToken.tsx @@ -34,7 +34,7 @@ const GitHubToken = () => { interactive: true, }, async (redirectUrl) => { - if (chrome.runtime.lastError || !redirectUrl) { + if (!redirectUrl) { console.error(chrome.runtime.lastError ? chrome.runtime.lastError.message : 'Authorization failed.'); return; } diff --git a/src/pages/Options/components/GiteeToken.tsx b/src/pages/Options/components/GiteeToken.tsx index e065a8c6..9e4d53ca 100644 --- a/src/pages/Options/components/GiteeToken.tsx +++ b/src/pages/Options/components/GiteeToken.tsx @@ -34,7 +34,7 @@ const GiteeToken = () => { interactive: true, }, async function (redirectUrl) { - if (chrome.runtime.lastError || !redirectUrl) { + if (!redirectUrl) { console.error(chrome.runtime.lastError ? chrome.runtime.lastError.message : 'Authorization failed.'); return; } From 0d0211bb2ff991d33aead551e29333c9890056e0 Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Wed, 13 Nov 2024 22:04:16 +0800 Subject: [PATCH 5/8] refactor: update UI Signed-off-by: frank-zsy --- src/api/githubApi.ts | 3 + src/helpers/gitee-token.ts | 48 +++++-- src/helpers/github-token.ts | 12 +- src/locales/en/translation.json | 69 +++------- src/locales/zh_CN/translation.json | 69 +++------- src/pages/Options/Options.css | 4 +- src/pages/Options/components/GitHubToken.tsx | 136 +++++------------- src/pages/Options/components/GiteeToken.tsx | 137 ++++++------------- 8 files changed, 171 insertions(+), 307 deletions(-) diff --git a/src/api/githubApi.ts b/src/api/githubApi.ts index 3435ec37..f88e2b38 100644 --- a/src/api/githubApi.ts +++ b/src/api/githubApi.ts @@ -8,6 +8,9 @@ export const githubRequest = async (endpoint: string, options: RequestInit = {}) try { const response = await fetch(`https://api.github.com${endpoint}`, { + headers: { + Authorization: `Bearer ${token}`, + }, ...options, }); return response.json(); diff --git a/src/helpers/gitee-token.ts b/src/helpers/gitee-token.ts index adc2b088..74b14980 100644 --- a/src/helpers/gitee-token.ts +++ b/src/helpers/gitee-token.ts @@ -1,11 +1,43 @@ -export const saveGiteeToken = (token: string) => { - chrome.storage.sync.set({ gitee_token: token }); -}; +const GITEE_TOKEN_KEY = 'gitee_token'; -export const getGiteeToken = (): Promise => { - return new Promise((resolve) => { - chrome.storage.sync.get('gitee_token', (result) => { - resolve(result.gitee_token || null); - }); +export const saveGiteeToken = (token: string, expireAt: number, refreshToken: string) => { + return chrome.storage.sync.set({ + [GITEE_TOKEN_KEY]: { + token, + expireAt, + refreshToken, + }, }); }; + +export const getGiteeToken = async (): Promise => { + const result = await chrome.storage.sync.get(GITEE_TOKEN_KEY); + if (!result || !result[GITEE_TOKEN_KEY]) { + return null; + } + const tokenInfo = result[GITEE_TOKEN_KEY]; + if (!tokenInfo.expireAt || tokenInfo.expireAt > Date.now()) { + return tokenInfo.token || null; + } else { + console.log('Gitee token expired and need refesh'); + const refreshReq = await fetch( + `https://gitee.com/oauth/token?grant_type=refresh_token&refresh_token=${tokenInfo.refreshToken}`, + { method: 'POST' } + ); + const refreshData = await refreshReq.json(); + if (!refreshData) { + console.log('Gitee token refresh failed'); + return null; + } + await saveGiteeToken( + refreshData.access_token, + Date.now() + (refreshData.expires_in - 120) * 1000, + refreshData.refresh_token + ); + return refreshData.access_token; + } +}; + +export const removeGiteeToken = () => { + return chrome.storage.sync.remove(GITEE_TOKEN_KEY); +}; diff --git a/src/helpers/github-token.ts b/src/helpers/github-token.ts index caabb328..0cdf4c5f 100644 --- a/src/helpers/github-token.ts +++ b/src/helpers/github-token.ts @@ -1,11 +1,17 @@ +const GITHUB_TOKEN_KEY = 'github_token'; + export const saveGithubToken = (token: string) => { - chrome.storage.sync.set({ github_token: token }); + return chrome.storage.sync.set({ [GITHUB_TOKEN_KEY]: token }); }; export const getGithubToken = (): Promise => { return new Promise((resolve) => { - chrome.storage.sync.get('github_token', (result) => { - resolve(result.github_token || null); + chrome.storage.sync.get(GITHUB_TOKEN_KEY, (result) => { + resolve(result[GITHUB_TOKEN_KEY] || null); }); }); }; + +export const removeGithubToken = () => { + return chrome.storage.sync.remove(GITHUB_TOKEN_KEY); +}; diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index b14497e3..d3e69495 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -73,52 +73,25 @@ "openrank_icon": "openrank", "contributors_participants_icon": "contributors and participants", "merged_lines_icon": "code lines change", - "github_token_configuration": "GitHub Token Configuration", - "github_token_tooltip": "Enter your GitHub Token here for authentication.", - "github_token_description": "Providing a GitHub Token ensures that HyperCRX can securely and effectively access and operate on your GitHub data for personalized analysis.", - "github_token_how_to_generate": "How to generate a GitHub Token?", - "github_token_step1": "1. Log in to GitHub and go to Settings.", - "github_token_step2": "2. Select Developer settings.", - "github_token_step3": "3. Choose Personal access tokens, click Tokens (classic), then Generate a personal access token.", - "github_token_step4": "4. Set token details:", - "github_token_note": "Note", - "github_token_note_description": "A descriptive name, e.g., \"HyperCRX Token\".", - "github_token_expiration": "Expiration", - "github_token_expiration_description": "Choose the validity period.", - "github_token_scopes": "Scopes", - "github_token_scopes_description": "Select permission scopes, such as repo and workflow.", - "github_token_step5": "5. Click the Generate token button.", - "github_token_step6": "6. Copy the generated token and paste it into the input box below.", - "github_token_placeholder": "GitHub Token", - "github_token_save": "Save", - "github_token_edit": "Edit", - "github_token_test": "Test Token", - "github_token_error_empty": "Token cannot be empty", - "github_token_success_save": "Token saved successfully", - "github_token_success_valid": "Token is valid. Username: {{username}}", - "github_token_error_invalid": "Invalid token or request failed", - "gitee_token_configuration": "Gitee Token Configuration", - "gitee_token_tooltip": "Enter your Gitee Token here for authentication.", - "gitee_token_description": "Providing a Gitee Token ensures that HyperCRX can securely and effectively access and operate on your Gitee data for personalized analysis.", - "gitee_token_how_to_generate": "How to generate a Gitee Token?", - "gitee_token_step1": "1. Log in to Gitee and go to Personal Settings.", - "gitee_token_step2": "2. Find and click on the Private Token option in the left menu.", - "gitee_token_step3": "3. Click the Generate New Token button.", - "gitee_token_step4": "4. Set token details:", - "gitee_token_note": "Note", - "gitee_token_note_description": "A descriptive name, e.g., \"HyperCRX Token\".", - "gitee_token_scopes": "Scopes", - "gitee_token_scopes_description": "Select permission scopes, such as user_info and projects.", - "gitee_token_step5": "5. Click the Generate token button.", - "gitee_token_step6": "6. Copy the generated token and paste it into the input box below.", - "gitee_token_placeholder": "Gitee Token", - "gitee_token_save": "Save", - "gitee_token_edit": "Edit", - "gitee_token_test": "Test Token", - "gitee_token_error_empty": "Token cannot be empty", - "gitee_token_success_save": "Token saved successfully", - "gitee_token_success_valid": "Token is valid. Username: {{username}}", - "gitee_token_error_invalid": "Invalid token or request failed", + + "github_account_configuration": "GitHub Account Binding", + "github_account_tooltip": "Authorize HyperCRX to access your GitHub account.", + "github_account_description": "Authorizing your GitHub account allows HyperCRX to securely and efficiently access GitHub data and perform other operations on your behalf, such as FastPR. The authorization information will only be securely stored in your browser; HyperCRX will not store or leak your authorization information.", + "github_account_bind": "Bind Account", + "github_account_unbind": "Unbind Account", + "github_account_no_bind": "No binding account found.", + "github_account_binded": "🎉Bind with GitHub account: {{username}}", + "github_account_bind_fail": "GitHub account binding failed.", + + "gitee_account_configuration": "Gitee Account Binding", + "gitee_account_tooltip": "Authorize HyperCRX to access your Gitee account.", + "gitee_account_description": "Authorizing your Gitee account allows HyperCRX to securely and efficiently access Gitee data and perform other operations on your behalf, such as FastPR. The authorization information will only be securely stored in your browser; HyperCRX will not store or leak your authorization information.", + "gitee_account_bind": "Bind Account", + "gitee_account_unbind": "Unbind Account", + "gitee_account_no_bind": "No binding account found.", + "gitee_account_binded": "🎉Bind with Gitee account: {{username}}", + "gitee_account_bind_fail": "Gitee account binding failed.", + "fast_pr": "FastPR", "pr_title_label": "PR Title", "pr_title_rule": "Please enter the PR title", @@ -151,6 +124,6 @@ "success_create_pr": "PR created successfully.\nPR URL: {{url}}", "error_get_user_info": "Failed to get user info: {{status}}", "error_get_file": "The path is a directory, not a file.", - "github_token_not_found": "GitHub token not found.\nPlease enter the token in the Settings page of the extension.", - "gitee_token_not_found": "Gitee token not found.\nPlease enter the token in the Settings page of the extension." + "github_token_not_found": "GitHub token not found.\nPlease bind your account in the Settings page of the extension.", + "gitee_token_not_found": "Gitee token not found.\nPlease bind your account in the Settings page of the extension." } diff --git a/src/locales/zh_CN/translation.json b/src/locales/zh_CN/translation.json index 3f912aea..36dee6a4 100644 --- a/src/locales/zh_CN/translation.json +++ b/src/locales/zh_CN/translation.json @@ -72,52 +72,25 @@ "openrank_icon": "openrank值", "contributors_participants_icon": "contributors和participants数", "merged_lines_icon": "代码变化量", - "github_token_configuration": "GitHub 令牌配置", - "github_token_tooltip": "在此输入您的 GitHub 令牌以进行身份验证。", - "github_token_description": "提供 GitHub 令牌可确保 HyperCRX 能够安全有效地访问和操作您的 GitHub 数据以进行个性化分析。", - "github_token_how_to_generate": "如何生成 GitHub 令牌?", - "github_token_step1": "1. 登录 GitHub 并进入设置。", - "github_token_step2": "2. 选择开发者设置。", - "github_token_step3": "3. 选择个人访问令牌,Tokens (classic),然后生成个人访问令牌。", - "github_token_step4": "4. 设置令牌详细信息:", - "github_token_note": "注意", - "github_token_note_description": "描述性名称,例如:“HyperCRX Token”。", - "github_token_expiration": "过期时间", - "github_token_expiration_description": "选择有效期。", - "github_token_scopes": "权限范围", - "github_token_scopes_description": "选择权限范围,例如 repo 和 workflow。", - "github_token_step5": "5. 点击生成令牌按钮。", - "github_token_step6": "6. 复制生成的令牌并将其粘贴到下面的输入框中。", - "github_token_placeholder": "GitHub 令牌", - "github_token_save": "保存", - "github_token_edit": "编辑", - "github_token_test": "测试令牌", - "github_token_error_empty": "令牌不能为空", - "github_token_success_save": "令牌保存成功", - "github_token_success_valid": "令牌有效。用户名:{{username}}", - "github_token_error_invalid": "令牌无效或请求失败", - "gitee_token_configuration": "Gitee 令牌配置", - "gitee_token_tooltip": "在此输入您的 Gitee 令牌以进行身份验证。", - "gitee_token_description": "提供 Gitee 令牌可确保 HyperCRX 能够安全有效地访问和操作您的 Gitee 数据以进行个性化分析。", - "gitee_token_how_to_generate": "如何生成 Gitee 令牌?", - "gitee_token_step1": "1. 登录 Gitee 并进入设置。", - "gitee_token_step2": "2. 在左侧菜单中找到并点击 私人令牌 选项。", - "gitee_token_step3": "3. 点击 生成新令牌 按钮。", - "gitee_token_step4": "4. 设置令牌详细信息:", - "gitee_token_note": "注意", - "gitee_token_note_description": "描述性名称,例如:“HyperCRX Token”。", - "gitee_token_scopes": "权限范围", - "gitee_token_scopes_description": "选择权限范围,例如 user_info 和 projects。", - "gitee_token_step5": "5. 点击生成令牌按钮。", - "gitee_token_step6": "6. 复制生成的令牌并将其粘贴到下面的输入框中。", - "gitee_token_placeholder": "Gitee 令牌", - "gitee_token_save": "保存", - "gitee_token_edit": "编辑", - "gitee_token_test": "测试令牌", - "gitee_token_error_empty": "令牌不能为空", - "gitee_token_success_save": "令牌保存成功", - "gitee_token_success_valid": "令牌有效。用户名:{{username}}", - "gitee_token_error_invalid": "令牌无效或请求失败", + + "github_account_configuration": "绑定 GitHub 账号", + "github_account_tooltip": "绑定 GitHub 账号", + "github_account_description": "为 HyperCRX 授权您的 GitHub 账号,可用于安全高效获取 GitHub 数据以及代理您的一些操作,例如 FastPR 功能。授权信息将安全的存储在您的浏览器本地,HyperCRX 并不会远程存储和泄露您的授权信息。", + "github_account_bind": "绑定账号", + "github_account_unbind": "解绑账号", + "github_account_no_bind": "当前无绑定账号", + "github_account_binded": "🎉当前绑定的 GitHub 账号为:{{username}}", + "github_account_bind_fail": "GitHub 账号绑定失败", + + "gitee_account_configuration": "绑定 Gitee 账号", + "gitee_account_tooltip": "绑定 Gitee 账号", + "gitee_account_description": "为 HyperCRX 授权您的 Gitee 账号,可用于安全高效获取 Gitee 数据以及代理您的一些操作,例如 FastPR 功能。授权信息将安全的存储在您的浏览器本地,HyperCRX 并不会远程存储和泄露您的授权信息。", + "gitee_account_bind": "绑定账号", + "gitee_account_unbind": "解绑账号", + "gitee_account_no_bind": "当前无绑定账号", + "gitee_account_binded": "🎉当前绑定的 Gitee 账号为:{{username}}", + "gitee_account_bind_fail": "Gitee 账号绑定失败", + "fast_pr": "FastPR", "pr_title_label": "PR 标题", "pr_title_rule": "请输入 PR 的标题", @@ -150,6 +123,6 @@ "success_create_pr": "PR创建成功。\nPR 地址为: {{url}}", "error_get_user_info": "获取用户信息失败:{{status}}", "error_get_file": "该路径是一个目录,而不是文件", - "github_token_not_found": "GitHub 的 token 没有找到。\n请记得在扩展程序的设置页面输入 GitHub token。", - "gitee_token_not_found": "Gitee 的 token 没有找到。\n请记得在扩展程序的设置页面输入 Gitee token。" + "github_token_not_found": "GitHub 的 token 没有找到。\n请记得在扩展程序的设置页面中绑定您的账号。", + "gitee_token_not_found": "Gitee 的 token 没有找到。\n请记得在扩展程序的设置页面中绑定您的账号。" } diff --git a/src/pages/Options/Options.css b/src/pages/Options/Options.css index 1d1f3add..749682f5 100644 --- a/src/pages/Options/Options.css +++ b/src/pages/Options/Options.css @@ -102,8 +102,8 @@ a { } .token-options button { - padding: 8px 16px; - margin: 0 25px 20px; + padding: 8px 8px; + margin: 0 10px 20px; background-color: #007bff; color: #fff; border: none; diff --git a/src/pages/Options/components/GitHubToken.tsx b/src/pages/Options/components/GitHubToken.tsx index ea180211..a44f06d1 100644 --- a/src/pages/Options/components/GitHubToken.tsx +++ b/src/pages/Options/components/GitHubToken.tsx @@ -3,31 +3,41 @@ import TooltipTrigger from '../../../components/TooltipTrigger'; import { saveGithubToken, getGithubToken, githubRequest } from '../../../api/githubApi'; import { message } from 'antd'; import { useTranslation } from 'react-i18next'; +import { removeGithubToken } from '../../../helpers/github-token'; const GitHubToken = () => { - const [token, setToken] = useState(''); - const [isCollapsed, setIsCollapsed] = useState(true); - const [isEditing, setIsEditing] = useState(false); - const { t } = useTranslation(); + const [inputValue, setInputValue] = useState(''); + const { t, i18n } = useTranslation(); const inputRef = useRef(null); const fetchToken = async () => { const storedToken = await getGithubToken(); if (storedToken) { - setToken(storedToken); + updateInputValue(); } }; + + const updateInputValue = async () => { + const userData = await githubRequest('/user'); + if (userData && userData.login) { + setInputValue(t('github_account_binded', { username: userData.login })); + } + }; + useEffect(() => { fetchToken(); }, []); - const handleOAuthToken = async () => { + useEffect(() => { + fetchToken(); + }, [i18n.language]); + + const handleBindAccount = async () => { const clientId = 'Ov23liyofMsuQYwtfGLb'; const redirectUri = 'http://8.147.129.123/github'; const callback = chrome.identity.getRedirectURL(); const scope = encodeURIComponent('read:user'); const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=token&state=${callback}`; - console.log('[FastPR]: Start authorization...'); chrome.identity.launchWebAuthFlow( { url: authUrl, @@ -44,49 +54,20 @@ const GitHubToken = () => { return; } const retData = JSON.parse(decodeURIComponent(ret)); - const userDataReq = await fetch(`https://api.github.com/user`, { - headers: { Authorization: `Bearer ${retData.access_token}` }, - }); - - const userData = await userDataReq.json(); - if (userData === null || userData.message) { - showMessage(t('github_token_error_invalid'), 'error'); - } else { - console.log(`[FastPR]: Welcome: ${userData.login}`); + if (!retData.access_token) { + console.error('Invalid token data returned, check the server config'); + showMessage(t('github_account_bind_fail'), 'error'); + return; } + await saveGithubToken(retData.access_token); + updateInputValue(); } ); }; - const handleSave = () => { - if (!token.trim()) { - showMessage(t('github_token_error_empty'), 'error'); - return; - } - saveGithubToken(token); - showMessage(t('github_token_success_save'), 'success'); - setIsEditing(false); - }; - - const handleEdit = () => { - setIsEditing(true); - }; - - const handleTestToken = async () => { - const userData = await githubRequest('/user', { - headers: { Authorization: `Bearer ${token}` }, - }); - - if (userData === null || userData.message) { - showMessage(t('github_token_error_invalid'), 'error'); - } else { - showMessage(t('github_token_success_valid', { username: userData.login }), 'success'); - } - }; - - const obfuscateToken = (token: string): string => { - if (token.length <= 4) return token; - return `${token[0]}${'*'.repeat(token.length - 2)}${token[token.length - 1]}`; + const handleUnbindAccount = async () => { + await removeGithubToken(); + setInputValue(''); }; const showMessage = (content: string, type: 'success' | 'error') => { @@ -104,70 +85,25 @@ const GitHubToken = () => { return (
-

{t('github_token_configuration')}

- -
-

{t('github_token_description')}

-
-
setIsCollapsed(!isCollapsed)} - style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }} - > -

{t('github_token_how_to_generate')}

- {isCollapsed ? '▶' : '▼'} -
- {!isCollapsed && ( -
-
    -
  1. {t('github_token_step1')}
  2. -
  3. {t('github_token_step2')}
  4. -
  5. {t('github_token_step3')}
  6. -
  7. - {t('github_token_step4')} -
      -
    • - {t('github_token_note')}: {t('github_token_note_description')} -
    • -
    • - {t('github_token_expiration')}: {t('github_token_expiration_description')} -
    • -
    • - {t('github_token_scopes')}: {t('github_token_scopes_description')} -
    • -
    -
  8. -
  9. {t('github_token_step5')}
  10. -
  11. {t('github_token_step6')}
  12. -
-
- )} +

{t('github_account_configuration')}

+
+

{t('github_account_description')}

setToken(e.target.value)} - placeholder={t('github_token_placeholder')} + value={inputValue} + placeholder={t('github_account_no_bind')} style={{ marginRight: '10px', flex: 1 }} - disabled={!isEditing} + disabled={true} /> - {isEditing ? ( - - ) : ( - - )} - -
diff --git a/src/pages/Options/components/GiteeToken.tsx b/src/pages/Options/components/GiteeToken.tsx index 9e4d53ca..152938a8 100644 --- a/src/pages/Options/components/GiteeToken.tsx +++ b/src/pages/Options/components/GiteeToken.tsx @@ -3,31 +3,42 @@ import TooltipTrigger from '../../../components/TooltipTrigger'; import { saveGiteeToken, getGiteeToken, giteeRequest } from '../../../api/giteeApi'; import { message } from 'antd'; import { useTranslation } from 'react-i18next'; +import { removeGiteeToken } from '../../../helpers/gitee-token'; const GiteeToken = () => { - const [token, setToken] = useState(''); - const [isCollapsed, setIsCollapsed] = useState(true); - const [isEditing, setIsEditing] = useState(false); - const { t } = useTranslation(); + const [inputValue, setInputValue] = useState(''); + const { t, i18n } = useTranslation(); const inputRef = useRef(null); + const fetchToken = async () => { const storedToken = await getGiteeToken(); if (storedToken) { - setToken(storedToken); + updateInputValue(); + } + }; + + const updateInputValue = async () => { + const userData = await giteeRequest('user'); + if (userData && userData.login) { + setInputValue(t('gitee_account_binded', { username: userData.login })); } }; + useEffect(() => { fetchToken(); }, []); - const handleOAuthToken = async () => { + useEffect(() => { + fetchToken(); + }, [i18n.language]); + + const handleBindAccount = async () => { const clientId = 'e76727820aa539f3a59399d0bc48156df2057e81774617e433eeb49d1dad97b3'; const redirectUri = 'http://8.147.129.123/gitee'; const scope = encodeURIComponent('user_info'); const callback = chrome.identity.getRedirectURL(); const authUrl = `https://gitee.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${callback}`; - console.log('[FastPR]: Start authorization...'); chrome.identity.launchWebAuthFlow( { url: authUrl, @@ -41,53 +52,25 @@ const GiteeToken = () => { const ret = new URL(redirectUrl).searchParams.get('ret'); if (!ret) { console.error('Ret not returned in callback URL, check the server config'); + showMessage(t('gitee_account_bind_fail'), 'error'); return; } const retData = JSON.parse(decodeURIComponent(ret)); - const userDataReq = await fetch(`https://gitee.com/api/v5/user`, { - headers: { Authorization: `Bearer ${retData.access_token}` }, - }); - - const userData = await userDataReq.json(); - - if (userData === null || userData.message) { - showMessage(t('gitee_token_error_invalid'), 'error'); - } else { - console.log(`[FastPR]: Welcome: ${userData.login}`); + if (!retData.access_token || !retData.refresh_token || !retData.expires_in) { + console.error('Invalid token data returned, check the server config'); + showMessage(t('gitee_account_bind_fail'), 'error'); + return; } + const expireAt = Date.now() + (retData.expires_in - 120) * 1000; + await saveGiteeToken(retData.access_token, expireAt, retData.refresh_token); + updateInputValue(); } ); }; - const handleSave = () => { - if (!token.trim()) { - showMessage(t('gitee_token_error_empty'), 'error'); - return; - } - saveGiteeToken(token); - showMessage(t('gitee_token_success_save'), 'success'); - setIsEditing(false); - }; - - const handleEdit = () => { - setIsEditing(true); - }; - - const handleTestToken = async () => { - const userData = await giteeRequest('/user', { - headers: { access_token: `Bearer ${token}` }, - }); - - if (userData === null || userData.message) { - showMessage(t('gitee_token_error_invalid'), 'error'); - } else { - showMessage(t('gitee_token_success_valid', { username: userData.login }), 'success'); - } - }; - - const obfuscateToken = (token: string): string => { - if (token.length <= 4) return token; - return `${token[0]}${'*'.repeat(token.length - 2)}${token[token.length - 1]}`; + const handleUnbindAccount = async () => { + await removeGiteeToken(); + setInputValue(''); }; const showMessage = (content: string, type: 'success' | 'error') => { @@ -105,67 +88,25 @@ const GiteeToken = () => { return (
-

{t('gitee_token_configuration')}

- -
-

{t('gitee_token_description')}

-
-
setIsCollapsed(!isCollapsed)} - style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }} - > -

{t('gitee_token_how_to_generate')}

- {isCollapsed ? '▶' : '▼'} -
- {!isCollapsed && ( -
-
    -
  1. {t('gitee_token_step1')}
  2. -
  3. {t('gitee_token_step2')}
  4. -
  5. {t('gitee_token_step3')}
  6. -
  7. - {t('gitee_token_step4')} -
      -
    • - {t('gitee_token_note')}: {t('gitee_token_note_description')} -
    • -
    • - {t('gitee_token_scopes')}: {t('gitee_token_scopes_description')} -
    • -
    -
  8. -
  9. {t('gitee_token_step5')}
  10. -
  11. {t('gitee_token_step6')}
  12. -
-
- )} +

{t('gitee_account_configuration')}

+
+

{t('gitee_account_description')}

setToken(e.target.value)} - placeholder={t('gitee_token_placeholder')} + value={inputValue} + placeholder={t('gitee_account_no_bind')} style={{ marginRight: '10px', flex: 1 }} - disabled={!isEditing} + disabled={true} /> - {isEditing ? ( - - ) : ( - - )} - -
From 03734ad04ad1bc14f51a0af28825de0f881fc937 Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Thu, 14 Nov 2024 01:41:40 +0800 Subject: [PATCH 6/8] fix: update auth scope Signed-off-by: frank-zsy --- src/pages/Options/components/GitHubToken.tsx | 2 +- src/pages/Options/components/GiteeToken.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Options/components/GitHubToken.tsx b/src/pages/Options/components/GitHubToken.tsx index a44f06d1..0eb881d2 100644 --- a/src/pages/Options/components/GitHubToken.tsx +++ b/src/pages/Options/components/GitHubToken.tsx @@ -35,7 +35,7 @@ const GitHubToken = () => { const clientId = 'Ov23liyofMsuQYwtfGLb'; const redirectUri = 'http://8.147.129.123/github'; const callback = chrome.identity.getRedirectURL(); - const scope = encodeURIComponent('read:user'); + const scope = encodeURIComponent('read:user, public_repo'); const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=token&state=${callback}`; chrome.identity.launchWebAuthFlow( diff --git a/src/pages/Options/components/GiteeToken.tsx b/src/pages/Options/components/GiteeToken.tsx index 152938a8..cde7a256 100644 --- a/src/pages/Options/components/GiteeToken.tsx +++ b/src/pages/Options/components/GiteeToken.tsx @@ -35,7 +35,7 @@ const GiteeToken = () => { const handleBindAccount = async () => { const clientId = 'e76727820aa539f3a59399d0bc48156df2057e81774617e433eeb49d1dad97b3'; const redirectUri = 'http://8.147.129.123/gitee'; - const scope = encodeURIComponent('user_info'); + const scope = encodeURIComponent('user_info projects pull_requests issues notes'); const callback = chrome.identity.getRedirectURL(); const authUrl = `https://gitee.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${callback}`; From e47c09eb5259aa77b7efc786f3abb64afaa42db6 Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Mon, 18 Nov 2024 14:59:30 +0800 Subject: [PATCH 7/8] update oauth url Signed-off-by: frank-zsy --- src/pages/Options/components/GitHubToken.tsx | 2 +- src/pages/Options/components/GiteeToken.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Options/components/GitHubToken.tsx b/src/pages/Options/components/GitHubToken.tsx index 0eb881d2..4b3f9f4e 100644 --- a/src/pages/Options/components/GitHubToken.tsx +++ b/src/pages/Options/components/GitHubToken.tsx @@ -33,7 +33,7 @@ const GitHubToken = () => { const handleBindAccount = async () => { const clientId = 'Ov23liyofMsuQYwtfGLb'; - const redirectUri = 'http://8.147.129.123/github'; + const redirectUri = 'https://oauth.hypercrx.cn/github'; const callback = chrome.identity.getRedirectURL(); const scope = encodeURIComponent('read:user, public_repo'); const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=token&state=${callback}`; diff --git a/src/pages/Options/components/GiteeToken.tsx b/src/pages/Options/components/GiteeToken.tsx index cde7a256..97337ab7 100644 --- a/src/pages/Options/components/GiteeToken.tsx +++ b/src/pages/Options/components/GiteeToken.tsx @@ -34,7 +34,7 @@ const GiteeToken = () => { const handleBindAccount = async () => { const clientId = 'e76727820aa539f3a59399d0bc48156df2057e81774617e433eeb49d1dad97b3'; - const redirectUri = 'http://8.147.129.123/gitee'; + const redirectUri = 'https://oauth.hypercrx.cn/gitee'; const scope = encodeURIComponent('user_info projects pull_requests issues notes'); const callback = chrome.identity.getRedirectURL(); const authUrl = `https://gitee.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${callback}`; From 7e1570567b3a45fb5a77fc1eec8a2d21f51e3b16 Mon Sep 17 00:00:00 2001 From: frank-zsy Date: Mon, 18 Nov 2024 16:29:36 +0800 Subject: [PATCH 8/8] Update fast pr config rule file path Signed-off-by: frank-zsy --- src/constant.ts | 3 ++- src/pages/Sandbox/index.jsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/constant.ts b/src/constant.ts index c1b67491..913b4794 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -9,4 +9,5 @@ export const OSS_XLAB_ENDPOINT = 'https://oss.open-digger.cn'; export const HYPERTRONS_CRX_NEW_ISSUE = 'https://github.com/hypertrons/hypertrons-crx/issues/new/choose'; export const HYPERCRX_GITHUB = 'https://github.com/hypertrons/hypertrons-crx'; -export const OSS_URL = 'https://hypercrx-fastpr.oss-cn-beijing.aliyuncs.com/fast-pr-url-rules.cjs'; + +export const FAST_PR_CONFIG_URL = 'https://hypercrx.cn/configs/fast-pr-url-rules.cjs'; diff --git a/src/pages/Sandbox/index.jsx b/src/pages/Sandbox/index.jsx index ec1526a2..94d891d8 100644 --- a/src/pages/Sandbox/index.jsx +++ b/src/pages/Sandbox/index.jsx @@ -1,11 +1,11 @@ import React, { useEffect } from 'react'; -import { OSS_URL } from '../../constant'; +import { FAST_PR_CONFIG_URL } from '../../constant'; import { createRoot } from 'react-dom/client'; const SandboxApp = () => { useEffect(() => { const fetchAndExecuteScript = () => { - fetch(OSS_URL) + fetch(FAST_PR_CONFIG_URL) .then((response) => response.text()) .then((scriptContent) => { const func = new Function(scriptContent);