11import axios from "axios" ;
2+ import type { AxiosInstance , InternalAxiosRequestConfig } from "axios" ;
3+ import { lsGet } from "@/plugins/storage" ;
24import { request } from "../../request" ;
5+ import { requestReauth } from "./reauth" ;
36import type { Branch , CommitAuthor , RepoPage , RepoSummary , UserInfo } from "@/types/git" ;
47import type {
58 GitHubBranch ,
@@ -16,6 +19,39 @@ import type {
1619const BASE_URL_USER = "https://api.github.com/user" ;
1720const BASE_URL_REPO = "https://api.github.com/repos" ;
1821
22+ let client : AxiosInstance | null = null ;
23+
24+ type RetriableConfig = InternalAxiosRequestConfig & { _retried ?: boolean } ;
25+
26+ /** Injects the stored token per request and, after a 401, prompts for a fresh one and retries once. */
27+ export function attachGitHubInterceptors ( http : AxiosInstance ) : void {
28+ http . interceptors . request . use ( ( config ) => {
29+ const token = lsGet ( "authId" ) ;
30+ if ( token ) {
31+ config . headers . Authorization = `Bearer ${ token } ` ;
32+ }
33+ return config ;
34+ } ) ;
35+ http . interceptors . response . use ( undefined , async ( error : unknown ) => {
36+ if ( axios . isAxiosError ( error ) && error . response ?. status === 401 && error . config ) {
37+ const config = error . config as RetriableConfig ;
38+ if ( ! config . _retried && ( await requestReauth ( ) ) ) {
39+ config . _retried = true ;
40+ return http . request ( config ) ;
41+ }
42+ }
43+ throw error ;
44+ } ) ;
45+ }
46+
47+ function githubHttp ( ) : AxiosInstance {
48+ if ( ! client ) {
49+ client = axios . create ( ) ;
50+ attachGitHubInterceptors ( client ) ;
51+ }
52+ return client ;
53+ }
54+
1955function toRepoSummary ( repo : GitHubRepoSummary ) : RepoSummary {
2056 return {
2157 fullName : repo . full_name ,
@@ -26,7 +62,7 @@ function toRepoSummary(repo: GitHubRepoSummary): RepoSummary {
2662}
2763
2864export async function listRepositories ( page : number , perPage : number ) : Promise < RepoPage > {
29- const response = await axios . get < GitHubRepoSummary [ ] > (
65+ const response = await githubHttp ( ) . get < GitHubRepoSummary [ ] > (
3066 `${ BASE_URL_USER } /repos?sort=updated&direction=desc&page=${ page } &per_page=${ perPage } `
3167 ) ;
3268 return {
@@ -69,13 +105,13 @@ export async function searchRepositories(query: string, maxResults: number): Pro
69105}
70106
71107export async function listBranches ( repoFullName : string ) : Promise < Branch [ ] > {
72- const response = await axios . get < GitHubBranch [ ] > ( `${ BASE_URL_REPO } /${ repoFullName } /branches?per_page=999` ) ;
108+ const response = await githubHttp ( ) . get < GitHubBranch [ ] > ( `${ BASE_URL_REPO } /${ repoFullName } /branches?per_page=999` ) ;
73109 return response . data ;
74110}
75111
76112export async function listFiles ( repoFullName : string , branch : string ) : Promise < string [ ] > {
77113 try {
78- const response = await axios . get < GitHubFileTree > (
114+ const response = await githubHttp ( ) . get < GitHubFileTree > (
79115 `${ BASE_URL_REPO } /${ repoFullName } /git/trees/${ branch } ?recursive=1`
80116 ) ;
81117 return response . data . tree . filter ( ( entry ) => entry . type === "blob" ) . map ( ( entry ) => entry . path ) ;
@@ -90,7 +126,7 @@ export async function listFiles(repoFullName: string, branch: string): Promise<s
90126
91127export async function readFile ( repoFullName : string , branch : string , filePath : string ) : Promise < string | undefined > {
92128 const content = await request (
93- axios . get < GitHubContent > ( `${ BASE_URL_REPO } /${ repoFullName } /contents/${ filePath } ?ref=${ branch } ` )
129+ githubHttp ( ) . get < GitHubContent > ( `${ BASE_URL_REPO } /${ repoFullName } /contents/${ filePath } ?ref=${ branch } ` )
94130 ) ;
95131 return content ? decodeUnicode ( content . content ) : undefined ;
96132}
@@ -106,23 +142,23 @@ function decodeUnicode(str: string): string {
106142}
107143
108144export async function getUser ( ) : Promise < UserInfo | undefined > {
109- const user = await request ( axios . get < GitHubUser > ( BASE_URL_USER ) ) ;
145+ const user = await request ( githubHttp ( ) . get < GitHubUser > ( BASE_URL_USER ) ) ;
110146 if ( ! user ) {
111147 return undefined ;
112148 }
113- const emails = await request ( axios . get < GitHubEmail [ ] > ( `${ BASE_URL_USER } /public_emails` ) ) ;
149+ const emails = await request ( githubHttp ( ) . get < GitHubEmail [ ] > ( `${ BASE_URL_USER } /public_emails` ) ) ;
114150 const publicEmail = ( emails ?. find ( ( entry ) => entry . primary ) ?? emails ?. [ 0 ] ) ?. email ;
115151 const email = publicEmail ?? user . email ?? `${ user . id } +${ user . login } @users.noreply.github.com` ;
116152 return { username : user . login , displayName : user . name ?? user . login , email } ;
117153}
118154
119155export async function getLastCommit ( repoFullName : string , branch : string ) : Promise < GitHubBranch | undefined > {
120- return request ( axios . get < GitHubBranch > ( `${ BASE_URL_REPO } /${ repoFullName } /branches/${ branch } ` ) ) ;
156+ return request ( githubHttp ( ) . get < GitHubBranch > ( `${ BASE_URL_REPO } /${ repoFullName } /branches/${ branch } ` ) ) ;
121157}
122158
123159export async function createBlob ( repoFullName : string , content : string ) : Promise < GitHubShaResponse | undefined > {
124160 return request (
125- axios . post < GitHubShaResponse > ( `${ BASE_URL_REPO } /${ repoFullName } /git/blobs` , {
161+ githubHttp ( ) . post < GitHubShaResponse > ( `${ BASE_URL_REPO } /${ repoFullName } /git/blobs` , {
126162 content,
127163 encoding : "utf-8"
128164 } )
@@ -135,7 +171,7 @@ export async function createTree(
135171 tree : GitHubTreeInput [ ]
136172) : Promise < GitHubShaResponse | undefined > {
137173 return request (
138- axios . post < GitHubShaResponse > ( `${ BASE_URL_REPO } /${ repoFullName } /git/trees` , {
174+ githubHttp ( ) . post < GitHubShaResponse > ( `${ BASE_URL_REPO } /${ repoFullName } /git/trees` , {
139175 base_tree : baseTreeSha ,
140176 tree
141177 } )
@@ -150,7 +186,7 @@ export async function createCommit(
150186 treeSha : string
151187) : Promise < GitHubShaResponse | undefined > {
152188 return request (
153- axios . post < GitHubShaResponse > ( `${ BASE_URL_REPO } /${ repoFullName } /git/commits` , {
189+ githubHttp ( ) . post < GitHubShaResponse > ( `${ BASE_URL_REPO } /${ repoFullName } /git/commits` , {
154190 message,
155191 author,
156192 parents : [ parentSha ] ,
@@ -165,7 +201,7 @@ export async function updateBranchRef(
165201 commitSha : string
166202) : Promise < GitHubRef | undefined > {
167203 return request (
168- axios . post < GitHubRef > ( `${ BASE_URL_REPO } /${ repoFullName } /git/refs/heads/${ branch } ` , {
204+ githubHttp ( ) . post < GitHubRef > ( `${ BASE_URL_REPO } /${ repoFullName } /git/refs/heads/${ branch } ` , {
169205 ref : "refs/heads/" + branch ,
170206 sha : commitSha
171207 } )
0 commit comments