| 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; |
| } |
|
|
| |
| interface IRepoApi { |
| getContents(account: Account, path: string): Promise<RepoContent[]>; |
| getFileContent(account: Account, path: string): Promise<RepoContent>; |
| updateFile(account: Account, path: string, content: string, sha: string, message?: string): Promise<any>; |
| deleteFile(account: Account, path: string, sha: string, message?: string): Promise<any>; |
| createFile(account: Account, path: string, content: string, message?: string): Promise<any>; |
| createAsset(account: Account, path: string, content: Uint8Array, message?: string): Promise<any>; |
| } |
|
|
| |
| function arrayBufferToBase64(buffer: Uint8Array): Promise<string> { |
| return new Promise((resolve, reject) => { |
| const blob = new Blob([buffer]); |
| const reader = new FileReader(); |
|
|
| reader.onload = () => { |
| const dataUrl = reader.result as string; |
| |
| const base64 = dataUrl.split(',')[1]; |
| resolve(base64); |
| }; |
|
|
| reader.onerror = reject; |
| reader.readAsDataURL(blob); |
| }); |
| } |
|
|
| |
| class GitHubRepoApi implements IRepoApi { |
| async getContents(account: Account, path: string = ''): Promise<RepoContent[]> { |
| 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<RepoContent> { |
| 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 encodedContent = 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: encodedContent, message }) |
| } |
| ); |
| return handleResponse(response); |
| } |
|
|
|
|
|
|
| async createAsset(account: Account, path: string, content: Uint8Array, message?: string) { |
|
|
| const encodedContent = await arrayBufferToBase64(content); |
| 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: encodedContent, message }) |
| } |
| ); |
| return handleResponse(response); |
| } |
| } |
|
|
| |
| class HuggingFaceRepoApi implements IRepoApi { |
| async getContents(account: Account, path: string = ''): Promise<RepoContent[]> { |
| 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() || ''; |
| item.type = item.type === 'directory' ? 'dir' : 'file'; |
| return item; |
| }); |
| } |
|
|
| async getFileContent(account: Account, path: string): Promise<RepoContent> { |
| const url = `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/raw/${account.ref}/${path}`; |
| const response = await fetch(url, |
| { |
| method: 'GET', |
| headers: { |
| Authorization: `Bearer ${account.token}` |
| } |
| } |
| ); |
|
|
| if (response.status === 401) { |
| localStorage.removeItem('isAuthenticated'); |
| localStorage.removeItem('token'); |
| router.push('/login'); |
| throw new Error('认证失败,请重新登录'); |
| } |
|
|
|
|
| |
| const contentType = response.headers.get('content-type'); |
| if (contentType && contentType.startsWith('image/')) { |
| return { |
| content: await arrayBufferToBase64(await response.bytes()), |
| download_url: null, |
| encoding: 'base64', |
| name: '', |
| path: path, |
| sha: '', |
| size: 0, |
| url: '', |
| html_url: '', |
| git_url: '', |
| type: 'file' |
| }; |
| } |
| 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({ |
| "lfs": false, |
| "path": path, |
| "content": content, |
| "message": message, |
| }) |
| } |
| ); |
| 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({ |
| "lfs": false, |
| "path": path, |
| "message": message, |
| }) |
| } |
| ); |
| 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({ |
| "lfs": false, |
| "path": path, |
| "content": content, |
| "message": message, |
| }) |
| } |
| ); |
| const result = await handleResponse(response); |
| return { |
| content: { |
| sha: result.commitOid |
| } |
| }; |
| } |
|
|
| async createAsset(account: Account, path: string, content: Uint8Array, message?: string) { |
| const encodedContent = await arrayBufferToBase64(content); |
| 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({ |
| "lfs": true, |
| "path": path, |
| "content": encodedContent, |
| "message": message, |
| }) |
| } |
| ); |
| const result = await handleResponse(response); |
| return { |
| content: { |
| sha: result.commitOid |
| } |
| }; |
| } |
| } |
|
|
| |
| 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}`); |
| } |
| } |
| } |
|
|
| |
| 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<RepoContent> { |
| 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); |
| }, |
|
|
| async createAsset(account: Account, path: string, content: Uint8Array, message?: string) { |
| const api = RepoApiFactory.getRepoApi(account.type); |
| return api.createAsset(account, path, content, message); |
| } |
| }; |
|
|