mahdahar ec5f2fc385 feat(gitea): add database-backed sync, API, and dashboard views
Add Gitea sync service with full and incremental modes, paged API fetch, upsert logic for users/repos/commits/PRs, and error aggregation.

Add migration for git_users, git_repositories, git_commits, git_pull_requests with indexes and unique constraints; add models and sync scripts for full/incremental jobs.

Update Gitea UI and dashboard filters (user/repo/date), aggregate commit loading across repositories, and wire routes/controllers/sidebar for dashboard and sync endpoints.
2026-04-22 16:39:30 +07:00

237 lines
6.3 KiB
PHP

<?php
namespace App\Controllers\Api;
use App\Controllers\BaseController;
use App\Libraries\GiteaSyncService;
use CodeIgniter\API\ResponseTrait;
class GitApi extends BaseController
{
use ResponseTrait;
private function ensureLoggedIn()
{
if (!session()->get('userid')) {
return $this->respond([
'status' => 'error',
'message' => 'Unauthorized',
], 401);
}
return null;
}
private function ensureAdmin()
{
$level = (int) session()->get('level');
if (!in_array($level, [0, 1, 2], true)) {
return $this->respond([
'status' => 'error',
'message' => 'Forbidden. Admin only.',
], 403);
}
return null;
}
public function summary()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$db = \Config\Database::connect();
$usersCount = $db->table('git_users')->countAllResults();
$repositoriesCount = $db->table('git_repositories')->countAllResults();
$commitsCount = $db->table('git_commits')->countAllResults();
$pullRequestsCount = $db->table('git_pull_requests')->countAllResults();
$latestCommit = $db->table('git_commits')->selectMax('committed_at', 'latest')->get()->getRowArray();
$latestPullRequest = $db->table('git_pull_requests')->selectMax('updated_at_gitea', 'latest')->get()->getRowArray();
$latestRepoSync = $db->table('git_repositories')->selectMax('last_synced_at', 'latest')->get()->getRowArray();
return $this->respond([
'status' => 'success',
'message' => 'Summary fetched',
'data' => [
'users' => $usersCount,
'repositories' => $repositoriesCount,
'commits' => $commitsCount,
'pull_requests' => $pullRequestsCount,
'latest_commit_at' => $latestCommit['latest'] ?? null,
'latest_pull_request_at' => $latestPullRequest['latest'] ?? null,
'last_synced_at' => $latestRepoSync['latest'] ?? null,
],
], 200);
}
public function users()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$db = \Config\Database::connect();
$rows = $db->table('git_users')
->select('id, gitea_user_id, username, full_name, email, is_active, is_admin, last_synced_at')
->orderBy('username', 'ASC')
->get()
->getResultArray();
return $this->respond([
'status' => 'success',
'message' => 'Users fetched',
'data' => $rows,
], 200);
}
public function repositories()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$db = \Config\Database::connect();
$rows = $db->table('git_repositories r')
->select('r.id, r.gitea_repo_id, r.name, r.full_name, r.owner_username, r.default_branch, r.is_private, r.last_pushed_at, r.last_synced_at, u.username as owner_login')
->join('git_users u', 'u.id = r.owner_user_id', 'left')
->orderBy('r.full_name', 'ASC')
->get()
->getResultArray();
return $this->respond([
'status' => 'success',
'message' => 'Repositories fetched',
'data' => $rows,
], 200);
}
public function commits()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$repoId = $this->request->getGet('repo_id');
$userId = $this->request->getGet('user_id');
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$limit = (int) ($this->request->getGet('limit') ?? 200);
if ($limit <= 0 || $limit > 1000) {
$limit = 200;
}
$db = \Config\Database::connect();
$builder = $db->table('git_commits c')
->select('c.id, c.sha, c.short_sha, c.message, c.author_name, c.author_email, c.committed_at, c.html_url, r.id as repository_id, r.full_name as repository_full_name, u.id as user_id, u.username as user_username')
->join('git_repositories r', 'r.id = c.repository_id', 'inner')
->join('git_users u', 'u.id = c.author_user_id', 'left');
if (!empty($repoId)) {
$builder->where('c.repository_id', (int) $repoId);
}
if (!empty($userId)) {
$builder->where('c.author_user_id', (int) $userId);
}
if (!empty($startDate)) {
$builder->where('c.committed_at >=', $startDate . ' 00:00:00');
}
if (!empty($endDate)) {
$builder->where('c.committed_at <=', $endDate . ' 23:59:59');
}
$rows = $builder
->orderBy('c.committed_at', 'DESC')
->limit($limit)
->get()
->getResultArray();
return $this->respond([
'status' => 'success',
'message' => 'Commits fetched',
'data' => $rows,
], 200);
}
public function pullRequests()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$repoId = $this->request->getGet('repo_id');
$userId = $this->request->getGet('user_id');
$state = $this->request->getGet('state');
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$limit = (int) ($this->request->getGet('limit') ?? 200);
if ($limit <= 0 || $limit > 1000) {
$limit = 200;
}
$db = \Config\Database::connect();
$builder = $db->table('git_pull_requests p')
->select('p.id, p.number, p.title, p.state, p.is_draft, p.is_merged, p.created_at_gitea, p.updated_at_gitea, p.merged_at, p.closed_at, p.html_url, r.id as repository_id, r.full_name as repository_full_name, u.id as user_id, u.username as user_username')
->join('git_repositories r', 'r.id = p.repository_id', 'inner')
->join('git_users u', 'u.id = p.author_user_id', 'left');
if (!empty($repoId)) {
$builder->where('p.repository_id', (int) $repoId);
}
if (!empty($userId)) {
$builder->where('p.author_user_id', (int) $userId);
}
if (!empty($state)) {
$builder->where('p.state', $state);
}
if (!empty($startDate)) {
$builder->where('p.updated_at_gitea >=', $startDate . ' 00:00:00');
}
if (!empty($endDate)) {
$builder->where('p.updated_at_gitea <=', $endDate . ' 23:59:59');
}
$rows = $builder
->orderBy('p.updated_at_gitea', 'DESC')
->limit($limit)
->get()
->getResultArray();
return $this->respond([
'status' => 'success',
'message' => 'Pull requests fetched',
'data' => $rows,
], 200);
}
public function sync()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
if ($response = $this->ensureAdmin()) {
return $response;
}
$service = new GiteaSyncService();
$result = $service->syncAll();
$statusCode = $result['success'] ? 200 : 500;
return $this->respond([
'status' => $result['success'] ? 'success' : 'error',
'message' => $result['message'],
'data' => $result['stats'] ?? [],
], $statusCode);
}
}