Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions src/components/GitHubTokenPermissions.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React, { useId, useRef, useState } from 'react';
import { FileText, GitFork, Info, KeyRound, List, ShieldCheck, Star, Workflow } from 'lucide-react';
import { AlertTriangle, ArrowRight, FileText, GitFork, Info, KeyRound, List, ShieldCheck, Star, Workflow } from 'lucide-react';
import { useT } from '../i18n/useT';
import { cn } from '../lib/utils';

const CODE_SPAN_RE = /`([^`]+)`/g;

type TokenMode = 'fine-grained' | 'classic';

const CLASSIC_TOKEN_URL =
'https://github.com/settings/tokens/new?description=GithubStarsManager&scopes=public_repo,gist,user';
const FINE_GRAINED_TOKEN_URL = 'https://github.com/settings/personal-access-tokens/new';

const MODE_TABS: Array<{ mode: TokenMode; labelKey: string }> = [
{ mode: 'fine-grained', labelKey: 'loginScreen.token-permissions-tab-finegrained' },
{ mode: 'classic', labelKey: 'loginScreen.token-permissions-tab-classic' },
{ mode: 'fine-grained', labelKey: 'loginScreen.token-permissions-tab-finegrained' },
];

const FINE_GRAINED_ROWS = [
Expand Down Expand Up @@ -45,7 +49,7 @@ function renderWithHighlights(text: string): React.ReactNode[] {
export function GitHubTokenPermissions({ heading = true }: { heading?: boolean }) {
const t = useT('login');
const baseId = useId();
const [mode, setMode] = useState<TokenMode>('fine-grained');
const [mode, setMode] = useState<TokenMode>('classic');
const tabRefs = useRef<Partial<Record<TokenMode, HTMLButtonElement | null>>>({});

const tabId = (tabMode: TokenMode) => `${baseId}-tab-${tabMode}`;
Expand Down Expand Up @@ -109,6 +113,33 @@ export function GitHubTokenPermissions({ heading = true }: { heading?: boolean }
hidden={mode !== tabMode}
tabIndex={0}
>
{tabMode === 'fine-grained' && (
<div className="mb-3.5 rounded-lg border border-warning/30 bg-warning/10 p-3 text-xs leading-relaxed text-foreground">
<div className="flex items-center gap-1.5 font-medium text-warning">
<AlertTriangle className="h-4 w-4 shrink-0" aria-hidden />
<span>{t('loginScreen.token-permissions-fine-warning-title')}</span>
</div>
<ul className="mt-2 list-disc space-y-1 pl-4 text-muted-foreground">
<li>{t('loginScreen.token-permissions-fine-warning-star')}</li>
<li>{t('loginScreen.token-permissions-fine-warning-lists')}</li>
</ul>
<div className="mt-2.5 flex flex-wrap items-center gap-1.5 border-t border-warning/20 pt-2 text-muted-foreground">
<span>{t('loginScreen.token-permissions-fine-warning-recommend')}</span>
<button
type="button"
onClick={() => {
setMode('classic');
tabRefs.current.classic?.focus();
}}
className="inline-flex items-center gap-1 font-semibold text-primary underline underline-offset-2 hover:text-primary/80"
>
{t('loginScreen.token-permissions-switch-to-classic')}
<ArrowRight className="h-3 w-3" aria-hidden />
</button>
</div>
</div>
)}

<ul className="space-y-2.5">
{rowsFor(tabMode).map(({ icon: Icon, featureKey, valueKey }) => (
<li key={featureKey + valueKey} className="flex gap-2.5">
Expand All @@ -133,7 +164,7 @@ export function GitHubTokenPermissions({ heading = true }: { heading?: boolean }
</div>

<a
href="https://github.com/settings/tokens"
href={mode === 'fine-grained' ? FINE_GRAINED_TOKEN_URL : CLASSIC_TOKEN_URL}
target="_blank"
rel="noopener noreferrer"
className="mt-4 inline-block font-medium text-primary hover:underline"
Expand Down
29 changes: 27 additions & 2 deletions src/components/LoginScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,38 @@ const enterBackendMode = async () => {
};

describe('LoginScreen token guidance', () => {
it('explains Star permissions before sign-in', () => {
it('explains Star permissions before sign-in and defaults to classic token', () => {
render(<TooltipProvider><LoginScreen /></TooltipProvider>);
expect(screen.getByText('GitHub Token 权限')).toBeVisible();
expect(screen.getByRole('tab', { name: '经典 Token' })).toHaveAttribute('aria-selected', 'true');
expect(screen.getByText('public_repo')).toBeVisible();

fireEvent.click(screen.getByRole('tab', { name: '细粒度 Token' }));
expect(screen.getByText('Starring')).toBeVisible();
fireEvent.click(screen.getByRole('tab', { name: '经典 Token' }));
});

it('shows limitation warning on fine-grained tab and allows switching to classic', () => {
render(<TooltipProvider><LoginScreen /></TooltipProvider>);
fireEvent.click(screen.getByRole('tab', { name: '细粒度 Token' }));
expect(screen.getByText('细粒度 Token 限制说明')).toBeVisible();
expect(screen.getByText(/无法稳定跨组织\/他人仓库 Star 或取消 Star/)).toBeVisible();
expect(screen.getByText(/不支持 Star 列表(Lists)双向同步/)).toBeVisible();

const switchBtn = screen.getByRole('button', { name: /经典 Token(点击切换)/ });
fireEvent.click(switchBtn);

expect(screen.getByRole('tab', { name: '经典 Token' })).toHaveAttribute('aria-selected', 'true');
expect(screen.getByText('public_repo')).toBeVisible();
});

it('adapts GitHub token creation link to the active mode', () => {
render(<TooltipProvider><LoginScreen /></TooltipProvider>);
const link = screen.getByRole('link', { name: '在GitHub上创建token →' });
expect(link).toHaveAttribute('href', 'https://github.com/settings/tokens/new?description=GithubStarsManager&scopes=public_repo,gist,user');

fireEvent.click(screen.getByRole('tab', { name: '细粒度 Token' }));
expect(link).toHaveAttribute('href', 'https://github.com/settings/personal-access-tokens/new');
});
});

describe('LoginScreen 后端登录', () => {
Expand Down
5 changes: 5 additions & 0 deletions src/locales/de/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` Schreiben",
"token-permissions-fine-fork-sync": "`Contents` Schreiben",
"token-permissions-fine-workflows": "`Actions` Schreiben",
"token-permissions-fine-warning-title": "Einschränkungen für fein abgestufte Token",
"token-permissions-fine-warning-star": "Kann Repositorys von Organisationen oder anderen Benutzern nicht zuverlässig mit Sternen versehen/entfernen (403-Fehler durch GitHub-Richtlinien)",
"token-permissions-fine-warning-lists": "Keine Synchronisierung von Star-Listen unterstützt (GitHub-API erfordert klassisches Token)",
"token-permissions-fine-warning-recommend": "Für die Verwaltung von Stars und Listen wird dringend empfohlen:",
"token-permissions-switch-to-classic": "Klassisches Token (klicken zum Wechseln)",
"token-permissions-classic-star": "`public_repo` (`repo` für private Repositorys)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/en/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` write",
"token-permissions-fine-fork-sync": "`Contents` write",
"token-permissions-fine-workflows": "`Actions` write",
"token-permissions-fine-warning-title": "Fine-grained Token Limitations",
"token-permissions-fine-warning-star": "Cannot reliably star/unstar repositories across organizations or other users (blocked with 403 by GitHub isolation & org policies)",
"token-permissions-fine-warning-lists": "Does not support Star Lists sync (GitHub API restriction, requires classic token)",
"token-permissions-fine-warning-recommend": "To manage stars and star lists, it is strongly recommended to use",
"token-permissions-switch-to-classic": "Classic Token (click to switch)",
"token-permissions-classic-star": "`public_repo` (`repo` for private repositories)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/es/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` escritura",
"token-permissions-fine-fork-sync": "`Contents` escritura",
"token-permissions-fine-workflows": "`Actions` escritura",
"token-permissions-fine-warning-title": "Limitaciones del token de permisos específicos",
"token-permissions-fine-warning-star": "No puede gestionar estrellas de forma fiable en repositorios de organizaciones u otros usuarios (error 403 por políticas de GitHub)",
"token-permissions-fine-warning-lists": "No admite la sincronización de listas de estrellas (la API de GitHub requiere un token clásico)",
"token-permissions-fine-warning-recommend": "Para gestionar estrellas y listas, se recomienda encarecidamente usar",
"token-permissions-switch-to-classic": "Token clásico (clic para cambiar)",
"token-permissions-classic-star": "`public_repo` (`repo` para repositorios privados)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/fr/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` écriture",
"token-permissions-fine-fork-sync": "`Contents` écriture",
"token-permissions-fine-workflows": "`Actions` écriture",
"token-permissions-fine-warning-title": "Limitations des jetons à granularité fine",
"token-permissions-fine-warning-star": "Ne peut pas ajouter/retirer d'étoiles de manière fiable sur les dépôts d'organisations ou d'autres utilisateurs (erreur 403 due aux restrictions GitHub)",
"token-permissions-fine-warning-lists": "Ne prend pas en charge la synchronisation des listes d'étoiles (l'API GitHub requiert un jeton classique)",
"token-permissions-fine-warning-recommend": "Pour gérer les étoiles et les listes, il est fortement recommandé d'utiliser",
"token-permissions-switch-to-classic": "Jeton classique (cliquer pour basculer)",
"token-permissions-classic-star": "`public_repo` (`repo` pour les dépôts privés)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/ja/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` 書き込み",
"token-permissions-fine-fork-sync": "`Contents` 書き込み",
"token-permissions-fine-workflows": "`Actions` 書き込み",
"token-permissions-fine-warning-title": "Fine-grained トークンの制限事項",
"token-permissions-fine-warning-star": "組織や他ユーザーのリポジトリで Star の追加・解除が失敗する場合があります(GitHub の権限隔離と組織ポリシーにより 403 エラー)",
"token-permissions-fine-warning-lists": "Star リストの同期に対応していません(GitHub API の制約によりクラシックトークンのみ対応)",
"token-permissions-fine-warning-recommend": "Star やリストを正常に管理するには、クラシックトークンの使用を強く推奨します:",
"token-permissions-switch-to-classic": "クラシックトークン(クリックで切り替え)",
"token-permissions-classic-star": "`public_repo`(非公開リポジトリには `repo`)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/ko/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` 쓰기",
"token-permissions-fine-fork-sync": "`Contents` 쓰기",
"token-permissions-fine-workflows": "`Actions` 쓰기",
"token-permissions-fine-warning-title": "세분화된 토큰 제한 사항",
"token-permissions-fine-warning-star": "조직 또는 타인의 저장소에서 Star 추가/취소가 차단될 수 있습니다(GitHub 권한 격리 및 조직 정책으로 인한 403 오류)",
"token-permissions-fine-warning-lists": "Star 목록 동기화를 지원하지 않습니다(GitHub API 제약으로 클래식 토큰만 지원)",
"token-permissions-fine-warning-recommend": "Star 및 Star 목록을 정상적으로 관리하려면 클래식 토큰 사용을 강력히 권장합니다:",
"token-permissions-switch-to-classic": "클래식 토큰(클릭하여 전환)",
"token-permissions-classic-star": "`public_repo`(비공개 저장소는 `repo`)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/pt-BR/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` gravação",
"token-permissions-fine-fork-sync": "`Contents` gravação",
"token-permissions-fine-workflows": "`Actions` gravação",
"token-permissions-fine-warning-title": "Limitações do token refinado",
"token-permissions-fine-warning-star": "Não pode adicionar/remover estrelas com segurança em repositórios de organizações ou terceiros (erro 403 por políticas do GitHub)",
"token-permissions-fine-warning-lists": "Não suporta sincronização de listas de estrelas (a API do GitHub exige token clássico)",
"token-permissions-fine-warning-recommend": "Para gerenciar estrelas e listas, é altamente recomendável usar",
"token-permissions-switch-to-classic": "Token clássico (clique para alternar)",
"token-permissions-classic-star": "`public_repo` (`repo` para repositórios privados)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/ru/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` запись",
"token-permissions-fine-fork-sync": "`Contents` запись",
"token-permissions-fine-workflows": "`Actions` запись",
"token-permissions-fine-warning-title": "Ограничения токенов с точной настройкой",
"token-permissions-fine-warning-star": "Не может надёжно ставить/снимать звёзды в репозиториях организаций или других пользователей (ошибка 403 из-за политик GitHub)",
"token-permissions-fine-warning-lists": "Не поддерживает синхронизацию списков звёзд (ограничение API GitHub, требуется классический токен)",
"token-permissions-fine-warning-recommend": "Для управления звёздами и списками настоятельно рекомендуется использовать",
"token-permissions-switch-to-classic": "Классический токен (нажмите для переключения)",
"token-permissions-classic-star": "`public_repo` (`repo` для приватных репозиториев)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/zh-TW/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` 寫入",
"token-permissions-fine-fork-sync": "`Contents` 寫入",
"token-permissions-fine-workflows": "`Actions` 寫入",
"token-permissions-fine-warning-title": "細粒度 Token 限制說明",
"token-permissions-fine-warning-star": "無法穩定跨組織/他人儲存庫 Star 或取消 Star(受 GitHub 權限隔離與組織策略限制,會報 403 權限錯誤)",
"token-permissions-fine-warning-lists": "不支援 Star 清單(Lists)雙向同步(GitHub 介面限制僅支援經典 Token)",
"token-permissions-fine-warning-recommend": "如需正常管理 Star 與 Star 清單,強烈建議使用",
"token-permissions-switch-to-classic": "經典 Token(點擊切換)",
"token-permissions-classic-star": "`public_repo`(私有儲存庫用 `repo`)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/zh/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"token-permissions-fine-gists": "`Gists` 写入",
"token-permissions-fine-fork-sync": "`Contents` 写入",
"token-permissions-fine-workflows": "`Actions` 写入",
"token-permissions-fine-warning-title": "细粒度 Token 限制说明",
"token-permissions-fine-warning-star": "无法稳定跨组织/他人仓库 Star 或取消 Star(受 GitHub 权限隔离与组织策略限制,会报 403 权限错误)",
"token-permissions-fine-warning-lists": "不支持 Star 列表(Lists)双向同步(GitHub 接口限制仅支持经典 Token)",
"token-permissions-fine-warning-recommend": "如需正常管理 Star 与 Star 列表,强烈建议使用",
"token-permissions-switch-to-classic": "经典 Token(点击切换)",
"token-permissions-classic-star": "`public_repo`(私有仓库用 `repo`)",
"token-permissions-classic-gists": "`gist`",
"token-permissions-classic-star-lists": "`user`",
Expand Down
Loading