feat: Add source code viewer with Gitea integration
- Add /source page with file explorer and code viewer - Connect to Gitea API for repository browsing - Implement MCP tools: get_repo_info, list_repo_files, read_repo_file, search_repo_code - Default to nucleo000/agent-ui repository - Support branch switching and file search
This commit is contained in:
136
server/index.ts
136
server/index.ts
@@ -861,6 +861,142 @@ Bun.serve({
|
||||
}, { headers: corsHeaders })
|
||||
}
|
||||
|
||||
// =====================
|
||||
// API de Gitea (Source Code Viewer)
|
||||
// =====================
|
||||
|
||||
// POST /api/gitea/repo - Connect and get repo info
|
||||
if (url.pathname === '/api/gitea/repo' && req.method === 'POST') {
|
||||
const body = await req.json()
|
||||
const { giteaUrl, username, password, owner, repo } = body
|
||||
|
||||
if (!giteaUrl || !username || !password || !owner || !repo) {
|
||||
return Response.json({ error: 'Missing required fields' }, { status: 400, headers: corsHeaders })
|
||||
}
|
||||
|
||||
try {
|
||||
const auth = Buffer.from(`${username}:${password}`).toString('base64')
|
||||
|
||||
// Get repo info
|
||||
const repoRes = await fetch(`${giteaUrl}/api/v1/repos/${owner}/${repo}`, {
|
||||
headers: { 'Authorization': `Basic ${auth}` }
|
||||
})
|
||||
|
||||
if (!repoRes.ok) {
|
||||
if (repoRes.status === 401) {
|
||||
return Response.json({ error: 'Invalid credentials' }, { status: 401, headers: corsHeaders })
|
||||
}
|
||||
if (repoRes.status === 404) {
|
||||
return Response.json({ error: 'Repository not found' }, { status: 404, headers: corsHeaders })
|
||||
}
|
||||
throw new Error('Failed to connect to Gitea')
|
||||
}
|
||||
|
||||
const repoData = await repoRes.json()
|
||||
|
||||
// Get branches
|
||||
const branchesRes = await fetch(`${giteaUrl}/api/v1/repos/${owner}/${repo}/branches`, {
|
||||
headers: { 'Authorization': `Basic ${auth}` }
|
||||
})
|
||||
|
||||
let branches = ['main']
|
||||
if (branchesRes.ok) {
|
||||
const branchesData = await branchesRes.json()
|
||||
branches = branchesData.map((b: any) => b.name)
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
repo: {
|
||||
name: repoData.name,
|
||||
description: repoData.description,
|
||||
default_branch: repoData.default_branch,
|
||||
stars_count: repoData.stars_count,
|
||||
forks_count: repoData.forks_count,
|
||||
owner: { login: repoData.owner?.login || owner }
|
||||
},
|
||||
branches
|
||||
}, { headers: corsHeaders })
|
||||
} catch (e: any) {
|
||||
return Response.json({ error: e.message }, { status: 500, headers: corsHeaders })
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/gitea/tree - Get file tree
|
||||
if (url.pathname === '/api/gitea/tree' && req.method === 'POST') {
|
||||
const body = await req.json()
|
||||
const { giteaUrl, username, password, owner, repo, branch, path } = body
|
||||
|
||||
try {
|
||||
const auth = Buffer.from(`${username}:${password}`).toString('base64')
|
||||
const apiPath = path ? `${giteaUrl}/api/v1/repos/${owner}/${repo}/contents/${path}?ref=${branch}`
|
||||
: `${giteaUrl}/api/v1/repos/${owner}/${repo}/contents?ref=${branch}`
|
||||
|
||||
const res = await fetch(apiPath, {
|
||||
headers: { 'Authorization': `Basic ${auth}` }
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to load tree')
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
const items = Array.isArray(data) ? data : [data]
|
||||
|
||||
const tree = items
|
||||
.map((item: any) => ({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: item.type === 'dir' ? 'dir' : 'file',
|
||||
children: item.type === 'dir' ? [] : undefined
|
||||
}))
|
||||
.sort((a: any, b: any) => {
|
||||
// Folders first, then files
|
||||
if (a.type !== b.type) return a.type === 'dir' ? -1 : 1
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
|
||||
return Response.json({ tree }, { headers: corsHeaders })
|
||||
} catch (e: any) {
|
||||
return Response.json({ error: e.message }, { status: 500, headers: corsHeaders })
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/gitea/file - Get file content
|
||||
if (url.pathname === '/api/gitea/file' && req.method === 'POST') {
|
||||
const body = await req.json()
|
||||
const { giteaUrl, username, password, owner, repo, branch, path } = body
|
||||
|
||||
try {
|
||||
const auth = Buffer.from(`${username}:${password}`).toString('base64')
|
||||
|
||||
const res = await fetch(
|
||||
`${giteaUrl}/api/v1/repos/${owner}/${repo}/contents/${path}?ref=${branch}`,
|
||||
{ headers: { 'Authorization': `Basic ${auth}` } }
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to load file')
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
|
||||
// Decode base64 content
|
||||
let content = ''
|
||||
if (data.content) {
|
||||
content = Buffer.from(data.content, 'base64').toString('utf-8')
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
content,
|
||||
encoding: data.encoding,
|
||||
size: data.size,
|
||||
sha: data.sha
|
||||
}, { headers: corsHeaders })
|
||||
} catch (e: any) {
|
||||
return Response.json({ error: e.message }, { status: 500, headers: corsHeaders })
|
||||
}
|
||||
}
|
||||
|
||||
// =====================
|
||||
// API de Database Explorer
|
||||
// =====================
|
||||
|
||||
Reference in New Issue
Block a user