import router from '../router'; import { API_BASE_URL, getHeaders, handleResponse } from './util'; export interface Account { owner: string; repo: string; ref: string; type: "github" | "hf"; token: string; } export interface RepoContent { name: string; path: string; sha: string; size: number; url: string; html_url: string; git_url: string; download_url: string | null; type: "file" | "dir"; content?: string; encoding?: string; } // Base interface for repo operations interface IRepoApi { getContents(account: Account, path: string): Promise; getFileContent(account: Account, path: string): Promise; updateFile(account: Account, path: string, content: string, sha: string, message?: string): Promise; deleteFile(account: Account, path: string, sha: string, message?: string): Promise; createFile(account: Account, path: string, content: string, message?: string): Promise; } // GitHub implementation class GitHubRepoApi implements IRepoApi { async getContents(account: Account, path: string = ''): Promise { const response = await fetch( `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}?ref=${account.ref}`, { headers: { Authorization: `Bearer ${account.token}` } } ); return await handleResponse(response); } async getFileContent(account: Account, path: string): Promise { const response = await fetch( `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}?ref=${account.ref}`, { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` } } ); return await handleResponse(response); } async updateFile(account: Account, path: string, content: string, sha: string, message?: string) { const encoder = new TextEncoder(); const bytes = encoder.encode(content); const base64Content = btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)])); const response = await fetch( `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` }, body: JSON.stringify({ branch: account.ref, content: base64Content, message, sha }) } ); return handleResponse(response); } async deleteFile(account: Account, path: string, sha: string, message?: string) { const response = await fetch( `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` }, body: JSON.stringify({ branch: account.ref, sha, message }) } ); return handleResponse(response); } async createFile(account: Account, path: string, content: string, message?: string) { const encoder = new TextEncoder(); const bytes = encoder.encode(content); const base64Content = btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)])); const response = await fetch( `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` }, body: JSON.stringify({ branch: account.ref, content: base64Content, message }) } ); return handleResponse(response); } } // HuggingFace implementation class HuggingFaceRepoApi implements IRepoApi { async getContents(account: Account, path: string = ''): Promise { const response = await fetch( `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/tree/${account.ref}/${path}`, { method: 'GET', headers: { Authorization: `Bearer ${account.token}` } } ); let result = await handleResponse(response); return result.map((item: any) => { item.sha = item.oid; item.name = item.path.split('/').pop() || ''; return item; }); } async getFileContent(account: Account, path: string): Promise { const response = await fetch( `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/raw/${account.ref}/${path}`, { method: 'GET', headers: { Authorization: `Bearer ${account.token}` } } ); if (response.status === 401) { localStorage.removeItem('isAuthenticated'); localStorage.removeItem('token'); router.push('/login'); throw new Error('认证失败,请重新登录'); } return { content: await response.text(), encoding: 'utf-8', name: '', path: path, sha: '', size: 0, url: '', html_url: '', git_url: '', download_url: null, type: 'file' }; } async updateFile(account: Account, path: string, content: string, sha: string, message?: string) { const response = await fetch( `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/commit/${account.ref}/${path}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` }, body: JSON.stringify({ "description": "", "summary": message, "files": [ { "content": content, "encoding": "utf-8", "path": path } ] }) } ); const result = await handleResponse(response); return { content: { sha: result.commitOid } }; } async deleteFile(account: Account, path: string, sha: string, message?: string) { const response = await fetch( `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/commit/${account.ref}/${path}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` }, body: JSON.stringify({ "description": "", "summary": message, "deletedFiles": [ { "path": path } ] }) } ); return handleResponse(response); } async createFile(account: Account, path: string, content: string, message?: string) { const response = await fetch( `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/commit/${account.ref}/${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${account.token}` }, body: JSON.stringify({ "description": "", "summary": message, "files": [ { "content": content, "encoding": "utf-8", "path": path } ] }) } ); const result = await handleResponse(response); return { content: { sha: result.commitOid } }; } } // Factory to get the appropriate repo API implementation class RepoApiFactory { private static githubInstance: GitHubRepoApi; private static huggingFaceInstance: HuggingFaceRepoApi; static getRepoApi(type: "github" | "hf"): IRepoApi { switch (type) { case "github": if (!this.githubInstance) { this.githubInstance = new GitHubRepoApi(); } return this.githubInstance; case "hf": if (!this.huggingFaceInstance) { this.huggingFaceInstance = new HuggingFaceRepoApi(); } return this.huggingFaceInstance; default: throw new Error(`Unsupported repository type: ${type}`); } } } // Exported API object that uses the factory export const repoApi = { async getContents(account: Account, path: string = '') { const api = RepoApiFactory.getRepoApi(account.type); return api.getContents(account, path); }, async getFileContent(account: Account, path: string): Promise { const api = RepoApiFactory.getRepoApi(account.type); return api.getFileContent(account, path); }, async updateFile(account: Account, path: string, content: string, sha: string, message?: string) { const api = RepoApiFactory.getRepoApi(account.type); return api.updateFile(account, path, content, sha, message); }, async deleteFile(account: Account, path: string, sha: string, message?: string) { const api = RepoApiFactory.getRepoApi(account.type); return api.deleteFile(account, path, sha, message); }, async createFile(account: Account, path: string, content: string, message?: string) { const api = RepoApiFactory.getRepoApi(account.type); return api.createFile(account, path, content, message); } };