Skip to content

Commit acf9fdd

Browse files
committed
fix
1 parent 0b366a7 commit acf9fdd

File tree

3 files changed

+208
-4
lines changed

3 files changed

+208
-4
lines changed

src/components/PatInputModal.jsx

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import {
2+
Modal,
3+
ModalOverlay,
4+
ModalContent,
5+
ModalHeader,
6+
ModalBody,
7+
ModalFooter,
8+
Button,
9+
Input,
10+
Text,
11+
VStack,
12+
Alert,
13+
AlertIcon,
14+
Link,
15+
Code,
16+
useToast,
17+
} from "@chakra-ui/react";
18+
import { useState } from "react";
19+
import { Icon } from "@iconify/react";
20+
21+
export function PatInputModal({ isOpen, onClose, onTokenSet }) {
22+
const [token, setToken] = useState("");
23+
const [isLoading, setIsLoading] = useState(false);
24+
const toast = useToast();
25+
26+
const handleSubmit = async () => {
27+
if (!token.trim()) {
28+
toast({
29+
title: "请输入 Personal Access Token",
30+
status: "warning",
31+
duration: 3000,
32+
isClosable: true,
33+
});
34+
return;
35+
}
36+
37+
// 验证 token 格式
38+
if (!token.startsWith("ghp_") && !token.startsWith("github_pat_")) {
39+
toast({
40+
title: "Token 格式错误",
41+
description: "Personal Access Token 应该以 'ghp_' 或 'github_pat_' 开头",
42+
status: "error",
43+
duration: 3000,
44+
isClosable: true,
45+
});
46+
return;
47+
}
48+
49+
setIsLoading(true);
50+
51+
try {
52+
// 测试 token 是否有效
53+
const response = await fetch("https://api.github.com/user", {
54+
headers: {
55+
Authorization: `token ${token}`,
56+
Accept: "application/vnd.github.v3+json",
57+
},
58+
});
59+
60+
if (response.ok) {
61+
onTokenSet(token);
62+
toast({
63+
title: "登录成功",
64+
description: "Personal Access Token 验证成功",
65+
status: "success",
66+
duration: 3000,
67+
isClosable: true,
68+
});
69+
onClose();
70+
setToken("");
71+
} else {
72+
throw new Error("Token 验证失败");
73+
}
74+
} catch (error) {
75+
toast({
76+
title: "Token 验证失败",
77+
description: "请检查 Token 是否正确或是否有足够的权限",
78+
status: "error",
79+
duration: 3000,
80+
isClosable: true,
81+
});
82+
} finally {
83+
setIsLoading(false);
84+
}
85+
};
86+
87+
const handleClose = () => {
88+
setToken("");
89+
onClose();
90+
};
91+
92+
return (
93+
<Modal isOpen={isOpen} onClose={handleClose} size="lg">
94+
<ModalOverlay />
95+
<ModalContent>
96+
<ModalHeader>
97+
<Icon icon="mingcute:key-line" style={{ display: "inline", marginRight: "8px" }} />
98+
设置 Personal Access Token
99+
</ModalHeader>
100+
<ModalBody>
101+
<VStack spacing={4} align="stretch">
102+
<Alert status="info">
103+
<AlertIcon />
104+
<Text fontSize="sm">
105+
由于这是纯前端项目,需要您提供 GitHub Personal Access Token 来完成认证。
106+
</Text>
107+
</Alert>
108+
109+
<VStack spacing={3} align="stretch">
110+
<Text fontSize="sm" fontWeight="medium">
111+
如何获取 Personal Access Token:
112+
</Text>
113+
<VStack spacing={2} align="stretch" pl={4}>
114+
<Text fontSize="xs">
115+
1. 前往{" "}
116+
<Link
117+
href="https://github.com/settings/tokens/new"
118+
color="blue.500"
119+
isExternal
120+
>
121+
GitHub Token 设置页面
122+
</Link>
123+
</Text>
124+
<Text fontSize="xs">
125+
2. 填写 Token 名称(如:ProfilePage Auth)
126+
</Text>
127+
<Text fontSize="xs">
128+
3. 选择权限:<Code fontSize="xs">repo</Code>(仓库权限)
129+
</Text>
130+
<Text fontSize="xs">
131+
4. 点击 "Generate token" 生成
132+
</Text>
133+
<Text fontSize="xs">
134+
5. 复制生成的 Token 并粘贴到下方输入框
135+
</Text>
136+
</VStack>
137+
</VStack>
138+
139+
<Input
140+
placeholder="粘贴您的 Personal Access Token(ghp_...)"
141+
value={token}
142+
onChange={(e) => setToken(e.target.value)}
143+
type="password"
144+
size="md"
145+
/>
146+
147+
<Alert status="warning" fontSize="xs">
148+
<AlertIcon />
149+
Token 将仅保存在您的浏览器本地,不会上传到任何服务器。
150+
</Alert>
151+
</VStack>
152+
</ModalBody>
153+
154+
<ModalFooter>
155+
<Button variant="ghost" mr={3} onClick={handleClose}>
156+
取消
157+
</Button>
158+
<Button
159+
colorScheme="blue"
160+
onClick={handleSubmit}
161+
isLoading={isLoading}
162+
loadingText="验证中..."
163+
leftIcon={<Icon icon="mingcute:check-line" />}
164+
>
165+
验证并登录
166+
</Button>
167+
</ModalFooter>
168+
</ModalContent>
169+
</Modal>
170+
);
171+
}

src/pages/ZonePage.jsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { AuthSection } from "../components/AuthSection";
1616
import { LoginButton } from "../components/LoginButton";
1717
import { PostEditor } from "../components/PostEditor";
1818
import { Timeline } from "../components/Timeline";
19+
import { PatInputModal } from "../components/PatInputModal";
1920
import { githubService } from "../services/zoneService";
2021

2122
function ZonePage() {
@@ -24,6 +25,11 @@ function ZonePage() {
2425
const [isAuthenticated, setIsAuthenticated] = useState(false);
2526
const [user, setUser] = useState(null);
2627
const { isOpen, onOpen, onClose } = useDisclosure();
28+
const {
29+
isOpen: isPatModalOpen,
30+
onOpen: onPatModalOpen,
31+
onClose: onPatModalClose
32+
} = useDisclosure();
2733

2834
useEffect(() => {
2935
loadPosts();
@@ -45,6 +51,17 @@ function ZonePage() {
4551
};
4652
}, []);
4753

54+
// 监听 PAT 输入模态框显示事件
55+
useEffect(() => {
56+
const handleShowPatModal = () => {
57+
onPatModalOpen();
58+
};
59+
window.addEventListener("show_pat_input_modal", handleShowPatModal);
60+
return () => {
61+
window.removeEventListener("show_pat_input_modal", handleShowPatModal);
62+
};
63+
}, [onPatModalOpen]);
64+
4865
const loadPosts = async () => {
4966
try {
5067
setLoading(true);
@@ -89,6 +106,11 @@ function ZonePage() {
89106
}
90107
};
91108

109+
const handleTokenSet = (token) => {
110+
githubService.setToken(token);
111+
checkAuth(); // 重新检查认证状态
112+
};
113+
92114
return (
93115
<motion.div
94116
initial={{ opacity: 0 }}
@@ -191,6 +213,13 @@ function ZonePage() {
191213
onClose={onClose}
192214
onSubmit={handlePostSubmit}
193215
/>
216+
217+
{/* PAT 输入模态框 */}
218+
<PatInputModal
219+
isOpen={isPatModalOpen}
220+
onClose={onPatModalClose}
221+
onTokenSet={handleTokenSet}
222+
/>
194223
</motion.div>
195224
);
196225
}

src/services/zoneService.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,8 @@ class GitHubService {
213213
}
214214

215215
// 由于是静态网站,我们需要使用 GitHub 的设备流程或者使用临时的代理服务
216-
// 这里我们暂时使用 Personal Access Token 的方式
217-
alert(
218-
"OAuth 回调成功!请在开发者工具控制台中设置你的 Personal Access Token"
219-
);
216+
// 这里我们触发显示 PAT 输入模态框
217+
this.triggerPatInputModal();
220218

221219
// 清理状态
222220
localStorage.removeItem("oauth_state");
@@ -227,6 +225,12 @@ class GitHubService {
227225
}
228226
}
229227

228+
// 触发显示 PAT 输入模态框
229+
triggerPatInputModal() {
230+
// 通过自定义事件通知组件显示 PAT 输入模态框
231+
window.dispatchEvent(new Event("show_pat_input_modal"));
232+
}
233+
230234
// 检查 URL 中是否有 OAuth 回调参数
231235
checkOAuthCallback() {
232236
const urlParams = new URLSearchParams(window.location.search);

0 commit comments

Comments
 (0)