codenuk_frontend_mine/src/lib/api/github.ts
2025-09-29 15:41:28 +05:30

234 lines
8.2 KiB
TypeScript

import { authApiClient } from '@/components/apis/authApiClients'
export interface AttachRepositoryPayload {
repository_url: string
branch_name?: string
user_id?: string
}
// -------- Repository contents --------
export interface RepoStructureEntry {
name: string
type: 'file' | 'directory'
size?: number
path: string
}
export interface RepoStructureResponse {
repository_id: string
directory_path: string
structure: RepoStructureEntry[]
}
export async function getRepositoryStructure(repositoryId: string, path: string = ''): Promise<RepoStructureResponse> {
const url = `/api/github/repository/${encodeURIComponent(repositoryId)}/structure${path ? `?path=${encodeURIComponent(path)}` : ''}`
const res = await authApiClient.get(url)
return res.data?.data as RepoStructureResponse
}
export interface FileContentResponse {
file_info: {
id: string
filename: string
file_extension?: string
relative_path: string
file_size_bytes?: number
mime_type?: string
is_binary?: boolean
language_detected?: string
line_count?: number
char_count?: number
}
content: string | null
preview?: string | null
}
export async function getRepositoryFileContent(repositoryId: string, filePath: string): Promise<FileContentResponse> {
const url = `/api/github/repository/${encodeURIComponent(repositoryId)}/file-content?file_path=${encodeURIComponent(filePath)}`
const res = await authApiClient.get(url)
return res.data?.data as FileContentResponse
}
export interface CommitSummaryResponse {
last_commit: {
hash: string
short_hash: string
author_name: string
author_email: string
committed_at: string
message: string
} | null
total_commits: number
}
export async function getRepositoryCommitSummary(repositoryId: string): Promise<CommitSummaryResponse> {
const url = `/api/github/repository/${encodeURIComponent(repositoryId)}/commit-summary`
const res = await authApiClient.get(url)
return res.data?.data as CommitSummaryResponse
}
export interface PathCommitResponse {
hash: string
short_hash: string
author_name: string
author_email: string
committed_at: string
message: string
path: string
}
export async function getPathCommit(repositoryId: string, relPath: string): Promise<PathCommitResponse | null> {
const url = `/api/github/repository/${encodeURIComponent(repositoryId)}/path-commit?path=${encodeURIComponent(relPath)}`
const res = await authApiClient.get(url)
return (res.data?.data as PathCommitResponse) || null
}
export interface CommitsListResponse {
items: Array<{
hash: string
short_hash: string
author_name: string
author_email: string
committed_at: string
message: string
}>
page: number
limit: number
total: number
has_next: boolean
}
export async function getRepositoryCommits(repositoryId: string, opts?: { page?: number; limit?: number; path?: string }): Promise<CommitsListResponse> {
const page = opts?.page ?? 1
const limit = opts?.limit ?? 20
const path = opts?.path ? `&path=${encodeURIComponent(opts.path)}` : ''
const url = `/api/github/repository/${encodeURIComponent(repositoryId)}/commits?page=${page}&limit=${limit}${path}`
const res = await authApiClient.get(url)
return res.data?.data as CommitsListResponse
}
export interface ResolvePathInfo {
repository_id: string
local_path: string
requested_file_path: string
resolved_absolute_path: string | null
exists: boolean
is_directory: boolean
}
export async function resolveRepositoryPath(repositoryId: string, filePath: string): Promise<ResolvePathInfo> {
const url = `/api/github/repository/${encodeURIComponent(repositoryId)}/resolve-path?file_path=${encodeURIComponent(filePath)}`
const res = await authApiClient.get(url)
return res.data?.data as ResolvePathInfo
}
export interface AttachRepositoryResponse<T = unknown> {
success: boolean
message?: string
data?: T
requires_auth?: boolean
auth_url?: string
auth_error?: boolean
}
export async function attachRepository(payload: AttachRepositoryPayload): Promise<AttachRepositoryResponse> {
// Add user_id as query fallback besides header for gateway caching/proxies
const rawUser = (typeof window !== 'undefined') ? localStorage.getItem('codenuk_user') : null
const userId = rawUser ? (JSON.parse(rawUser)?.id || null) : null
const url = userId ? `/api/github/attach-repository?user_id=${encodeURIComponent(userId)}` : '/api/github/attach-repository'
const response = await authApiClient.post(url, { ...payload, user_id: userId || payload.user_id }, {
headers: {
'Content-Type': 'application/json',
},
})
return response.data as AttachRepositoryResponse
}
export interface GitHubAuthStatusData {
connected: boolean
github_username?: string
github_user_id?: string
connected_at?: string
scopes?: string[]
requires_auth?: boolean
auth_url?: string
}
export async function getGitHubAuthStatus(): Promise<AttachRepositoryResponse<GitHubAuthStatusData>> {
const rawUser = (typeof window !== 'undefined') ? localStorage.getItem('codenuk_user') : null
const userId = rawUser ? (JSON.parse(rawUser)?.id || null) : null
const url = userId ? `/api/github/auth/github/status?user_id=${encodeURIComponent(userId)}` : '/api/github/auth/github/status'
const response = await authApiClient.get(url)
return response.data as AttachRepositoryResponse<GitHubAuthStatusData>
}
// -------- User repositories (UI-only helper) --------
export interface GitHubRepoSummary {
id?: string | number
full_name: string
name: string
owner?: { login?: string } | null
description?: string | null
visibility?: 'public' | 'private'
stargazers_count?: number
forks_count?: number
language?: string | null
updated_at?: string
html_url?: string
}
// Tries backend gateway route first. If backend does not yet provide it, returns an empty list gracefully.
export async function getUserRepositories(): Promise<GitHubRepoSummary[]> {
try {
const rawUser = (typeof window !== 'undefined') ? localStorage.getItem('codenuk_user') : null
const userId = rawUser ? (JSON.parse(rawUser)?.id || null) : null
// Prefer path param route; fallback to legacy query-based if gateway not updated
const primaryUrl = userId ? `/api/github/user/${encodeURIComponent(userId)}/repositories` : '/api/github/user/repositories'
let res
try {
res = await authApiClient.get(primaryUrl)
} catch (e: any) {
const fallbackUrl = userId ? `/api/github/user/repos?user_id=${encodeURIComponent(userId)}` : '/api/github/user/repos'
res = await authApiClient.get(fallbackUrl)
}
const data = res?.data?.data || res?.data
if (Array.isArray(data)) {
// If data already looks like GitHubRepoSummary, return as-is
if (data.length === 0) return []
const looksLike = (item: any) => item && (item.full_name || (item.name && item.owner))
if (looksLike(data[0])) return data as GitHubRepoSummary[]
// Normalize rows coming from github_repositories table with parsed metadata/codebase_analysis
const normalized = data.map((r: any) => {
const md = r?.metadata || {}
const owner = r?.owner_name || md?.owner?.login || (typeof md?.full_name === 'string' ? md.full_name.split('/')[0] : undefined)
const name = r?.repository_name || md?.name || (typeof md?.full_name === 'string' ? md.full_name.split('/')[1] : undefined) || r?.repo
const full = md?.full_name || (owner && name ? `${owner}/${name}` : r?.repository_url)
return {
id: r?.id,
full_name: full,
name: name,
owner: owner ? { login: owner } : undefined,
description: md?.description || null,
visibility: md?.visibility || (r?.is_public ? 'public' : 'private'),
stargazers_count: md?.stargazers_count || 0,
forks_count: md?.forks_count || 0,
language: md?.language || null,
updated_at: md?.updated_at || r?.updated_at,
html_url: md?.html_url || (full ? `https://github.com/${full}` : undefined),
} as GitHubRepoSummary
})
return normalized
}
return []
} catch (e: any) {
// If endpoint not found or unauthorized, surface as empty for now (UI design requirement)
const status = e?.response?.status
if (status === 404 || status === 401) return []
return []
}
}