fix(repoList): handle Bad credentials error from GitHub API

Catch 401 responses in useRepos loadMore and expose hasCredentialError,
then show a sign-in prompt in RepoList instead of an unhandled rejection.
This commit is contained in:
Julien Calixte
2026-04-27 10:32:37 +02:00
parent df3e217d01
commit da4fada8a1
2 changed files with 41 additions and 20 deletions

View File

@@ -9,6 +9,7 @@ const STALE_TIME_MS = 20 * 60 * 1000
const repos = ref<RepoBase[]>([]) const repos = ref<RepoBase[]>([])
const isReady = ref(false) const isReady = ref(false)
const hasCredentialError = ref(false)
const currentPage = ref(0) const currentPage = ref(0)
const totalCount = ref(0) const totalCount = ref(0)
let lastFetchedAt = 0 let lastFetchedAt = 0
@@ -21,24 +22,38 @@ export const useRepos = () => {
isReady.value = true isReady.value = true
return return
} }
const octokit = await getOctokit() try {
const nextPage = currentPage.value + 1 const octokit = await getOctokit()
const repoList = await octokit.request("GET /search/repositories", { const nextPage = currentPage.value + 1
q: `user:${username.value}`, const repoList = await octokit.request("GET /search/repositories", {
per_page: PER_PAGE, q: `user:${username.value}`,
page: nextPage per_page: PER_PAGE,
}) page: nextPage
currentPage.value = nextPage })
totalCount.value = repoList.data.total_count currentPage.value = nextPage
const newItems = repoList.data.items.map((item) => ({ totalCount.value = repoList.data.total_count
id: `${item.id}`, const newItems = repoList.data.items.map((item) => ({
name: item.name, id: `${item.id}`,
isPrivate: item.private name: item.name,
})) isPrivate: item.private
repos.value = [...repos.value, ...newItems].sort((a, b) => }))
a.name < b.name ? -1 : 1 repos.value = [...repos.value, ...newItems].sort((a, b) =>
) a.name < b.name ? -1 : 1
isReady.value = true )
} catch (err: unknown) {
if (
typeof err === "object" &&
err !== null &&
"status" in err &&
(err as { status: number }).status === 401
) {
hasCredentialError.value = true
} else {
throw err
}
} finally {
isReady.value = true
}
} }
const canLoadMore = computed(() => repos.value.length < totalCount.value) const canLoadMore = computed(() => repos.value.length < totalCount.value)
@@ -50,10 +65,11 @@ export const useRepos = () => {
currentPage.value = 0 currentPage.value = 0
totalCount.value = 0 totalCount.value = 0
isReady.value = false isReady.value = false
hasCredentialError.value = false
} }
lastFetchedAt = Date.now() lastFetchedAt = Date.now()
loadMore() loadMore()
} }
return { repos, isReady, canLoadMore, loadMore } return { repos, isReady, hasCredentialError, canLoadMore, loadMore }
} }

View File

@@ -2,12 +2,13 @@
import { vInfiniteScroll } from "@vueuse/components" import { vInfiniteScroll } from "@vueuse/components"
import GoBack from "@/components/GoBack.vue" import GoBack from "@/components/GoBack.vue"
import SignInGithub from "@/components/SignInGithub.vue"
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook" import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
import { useRepos } from "@/hooks/useRepos.hook" import { useRepos } from "@/hooks/useRepos.hook"
import { useRepoList } from "@/modules/repo/hooks/useRepoList.hook" import { useRepoList } from "@/modules/repo/hooks/useRepoList.hook"
const { username } = useGitHubLogin() const { username } = useGitHubLogin()
const { isReady } = useRepos() const { isReady, hasCredentialError } = useRepos()
const { const {
favoriteRepos, favoriteRepos,
otherRepos, otherRepos,
@@ -26,6 +27,10 @@ const {
<h1 class="title is-1">Repositories</h1> <h1 class="title is-1">Repositories</h1>
<go-back /> <go-back />
<div v-if="!isReady">loading...</div> <div v-if="!isReady">loading...</div>
<div v-else-if="hasCredentialError">
<p>Your GitHub credentials are invalid or expired.</p>
<sign-in-github />
</div>
<div v-else class="columns is-centered"> <div v-else class="columns is-centered">
<div class="column is-one-third"> <div class="column is-one-third">
<table <table