fix(gitea): 建仓需 write:user scope——实例支持改 Token + 权限提示补全
- Gitea 的 POST /user/repos 归 write:user scope(403 实测,R10:以实际 行为为准),此前权限指导只给了 read:user 导致新建仓库失败 - GiteaInstancesSection 新增「改 Token」内联编辑(此前无入口,删除实例 重建会级联丢失全部仓库绑定) - 添加实例的 placeholder 补权限要求说明 Rules-Applied: R08, R10
This commit is contained in:
parent
1d068d188c
commit
b7008a132e
@ -8,6 +8,7 @@ import {
|
||||
giteaSaveInstance,
|
||||
giteaListInstances,
|
||||
giteaDeleteInstance,
|
||||
giteaUpdateInstance,
|
||||
giteaTestConnection,
|
||||
giteaGetRepoLink,
|
||||
giteaListLinkedRepos,
|
||||
@ -46,6 +47,21 @@ export function GiteaInstancesSection() {
|
||||
const [instUrl, setInstUrl] = useState("");
|
||||
const [instToken, setInstToken] = useState("");
|
||||
const [testing, setTesting] = useState<string | null>(null);
|
||||
const [editingTokenId, setEditingTokenId] = useState<string | null>(null);
|
||||
const [newToken, setNewToken] = useState("");
|
||||
|
||||
const handleUpdateToken = async (id: string) => {
|
||||
if (!newToken.trim()) return;
|
||||
try {
|
||||
await giteaUpdateInstance(id, { access_token: newToken.trim() });
|
||||
qc.invalidateQueries({ queryKey: ["gitea-instances"] });
|
||||
setEditingTokenId(null);
|
||||
setNewToken("");
|
||||
showToast("Token 已更新 ✓ 可点「测试连接」验证");
|
||||
} catch (e) {
|
||||
showToast(`❌ ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAdd = async () => {
|
||||
if (!instName.trim() || !instUrl.trim() || !instToken.trim()) return;
|
||||
@ -105,7 +121,8 @@ export function GiteaInstancesSection() {
|
||||
<p className="text-xs text-gray-400">尚未添加 Gitea 实例——添加后可在各项目蓝图的治理面板中绑定仓库</p>
|
||||
)}
|
||||
{instances.map((inst) => (
|
||||
<div key={inst.id} className="flex items-center gap-2 text-xs">
|
||||
<div key={inst.id} className="space-y-1.5">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="font-medium text-gray-700 shrink-0">{inst.name}</span>
|
||||
{inst.is_default && (
|
||||
<span className="text-[10px] px-1 rounded bg-blue-50 text-blue-500 shrink-0">默认</span>
|
||||
@ -114,6 +131,12 @@ export function GiteaInstancesSection() {
|
||||
<button onClick={() => handleTest(inst)} disabled={testing === inst.id} className={btnCls}>
|
||||
{testing === inst.id ? "…" : "测试连接"}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setEditingTokenId(editingTokenId === inst.id ? null : inst.id); setNewToken(""); }}
|
||||
className={btnCls}
|
||||
>
|
||||
改 Token
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(inst)}
|
||||
className="px-2 py-1 rounded text-[11px] border border-gray-200 text-red-400 hover:bg-red-50"
|
||||
@ -121,12 +144,32 @@ export function GiteaInstancesSection() {
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
{editingTokenId === inst.id && (
|
||||
<div className="flex gap-1.5 pl-2">
|
||||
<input
|
||||
className={inputCls}
|
||||
type="password"
|
||||
placeholder="新 Access Token(需 repository 读写 + user 读写权限)"
|
||||
value={newToken}
|
||||
onChange={(e) => setNewToken(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleUpdateToken(inst.id)}
|
||||
disabled={!newToken.trim()}
|
||||
className="px-3 py-1.5 rounded text-xs bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 shrink-0"
|
||||
>
|
||||
更新
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{showAdd && (
|
||||
<div className="space-y-2 border-t border-gray-100 pt-3">
|
||||
<input className={inputCls} placeholder="实例名称(如:家里 NAS)" value={instName} onChange={(e) => setInstName(e.target.value)} />
|
||||
<input className={inputCls} placeholder="https://gitea.example.com" value={instUrl} onChange={(e) => setInstUrl(e.target.value)} />
|
||||
<input className={inputCls} type="password" placeholder="Access Token(用户设置 → 应用 → 生成令牌)" value={instToken} onChange={(e) => setInstToken(e.target.value)} />
|
||||
<input className={inputCls} type="password" placeholder="Access Token(用户设置 → 应用 → 生成令牌;权限:repository 读写 + user 读写)" value={instToken} onChange={(e) => setInstToken(e.target.value)} />
|
||||
<div className="flex gap-1.5">
|
||||
<button onClick={handleAdd} className="px-3 py-1.5 rounded text-xs bg-blue-600 text-white hover:bg-blue-700">保存</button>
|
||||
<button onClick={() => setShowAdd(false)} className="px-3 py-1.5 rounded text-xs border border-gray-200 text-gray-500 hover:bg-gray-50">取消</button>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user