import { customFetch } from "@/api/mutator"; // Forgejo Connection types export interface ForgejoConnection { id: string; organization_id: string; name: string; base_url: string; token: null; // Always null in responses active: boolean; has_token: boolean; token_last_eight: string | null; created_at: string; updated_at: string; } export interface ForgejoConnectionCreate { name: string; base_url: string; token: string; } export interface ForgejoConnectionUpdate { name?: string; base_url?: string; token?: string; } // Forgejo Repository types export interface ForgejoRepoLabel { id: number | null; name: string; color: string; description: string; } export interface ForgejoRepository { id: string; organization_id: string; connection_id: string; owner: string; repo: string; display_name: string; default_branch: string; active: boolean; has_webhook_secret: boolean; description: string | null; open_issues_count: number; is_archived: boolean; topics: string[]; labels: ForgejoRepoLabel[]; connection: ForgejoConnection; last_sync_at: string | null; last_sync_error: string | null; created_at: string; updated_at: string; } export interface ForgejoRepositoryCreate { connection_id: string; owner: string; repo: string; display_name?: string; default_branch?: string; } export interface ForgejoRepositoryUpdate { display_name?: string; default_branch?: string; webhook_secret?: string | null; } type ApiResponse = { data: T; status: number; headers: Headers; }; async function fetchJson(path: string, init?: RequestInit): Promise { const response = await customFetch>(path, init ?? {}); return response.data; } // Forgejo Connection API export async function getForgejoConnections(): Promise { return fetchJson("/api/v1/forgejo/connections"); } export async function createForgejoConnection( data: ForgejoConnectionCreate, ): Promise { return fetchJson("/api/v1/forgejo/connections", { method: "POST", body: JSON.stringify(data), }); } export async function getForgejoConnection( connectionId: string, ): Promise { return fetchJson( `/api/v1/forgejo/connections/${connectionId}`, ); } export async function updateForgejoConnection( connectionId: string, data: ForgejoConnectionUpdate, ): Promise { return fetchJson( `/api/v1/forgejo/connections/${connectionId}`, { method: "PATCH", body: JSON.stringify(data), }, ); } export async function deleteForgejoConnection( connectionId: string, ): Promise { await customFetch>( `/api/v1/forgejo/connections/${connectionId}`, { method: "DELETE", }, ); } // Forgejo Repository API export async function getForgejoRepositories(): Promise { return fetchJson("/api/v1/forgejo/repositories"); } export async function createForgejoRepository( data: ForgejoRepositoryCreate, ): Promise { return fetchJson("/api/v1/forgejo/repositories", { method: "POST", body: JSON.stringify(data), }); } export async function getForgejoRepository( repositoryId: string, ): Promise { return fetchJson( `/api/v1/forgejo/repositories/${repositoryId}`, ); } export async function updateForgejoRepository( repositoryId: string, data: ForgejoRepositoryUpdate, ): Promise { return fetchJson( `/api/v1/forgejo/repositories/${repositoryId}`, { method: "PATCH", body: JSON.stringify(data), }, ); } export async function deleteForgejoRepository( repositoryId: string, ): Promise { await customFetch>( `/api/v1/forgejo/repositories/${repositoryId}`, { method: "DELETE", }, ); } // Pre-save connection test export interface ForgejoConnectionTestRepo { full_name: string; name: string; owner: string; default_branch: string; private: boolean; description: string | null; } export interface ForgejoConnectionTestResult { valid: boolean; user_login: string | null; user_full_name: string | null; repo_count: number; repos: ForgejoConnectionTestRepo[]; error: string | null; response_time_ms: number; } export async function testForgejoConnection(data: { base_url: string; token: string; }): Promise { return fetchJson( "/api/v1/forgejo/connections/test", { method: "POST", body: JSON.stringify(data), }, ); } // Remote repo discovery export interface ForgejoRemoteRepo { full_name: string; name: string; owner: string; default_branch: string; description: string | null; private: boolean; html_url: string; } export async function getConnectionRepos( connectionId: string, ): Promise { return fetchJson( `/api/v1/forgejo/connections/${connectionId}/repos`, ); } // Forgejo Sync & Validation API export async function syncRepository(repositoryId: string): Promise<{ created: number; updated: number; open: number; closed: number; total: number; }> { return fetchJson<{ created: number; updated: number; open: number; closed: number; total: number; }>(`/api/v1/forgejo/repositories/${repositoryId}/sync`, { method: "POST", }); } export interface ForgejoValidationStatus { ok: boolean; status: string; error_message?: string | null; } export interface ForgejoConnectionValidationResponse { connection_id: string; status: ForgejoValidationStatus; response_time_ms: number; validated_at: string; } export interface ForgejoRepositoryValidationResponse { repository_id: string; status: ForgejoValidationStatus; response_time_ms: number; validated_at: string; repo_exists?: boolean | null; } export async function validateConnection( connectionId: string, ): Promise { return fetchJson( `/api/v1/forgejo/connections/${connectionId}/validate`, { method: "POST", }, ); } export async function validateRepository( repositoryId: string, ): Promise { return fetchJson( `/api/v1/forgejo/repositories/${repositoryId}/validate`, { method: "POST", }, ); } // Forgejo Issue types export interface ForgejoIssueLabel { id?: number | null; name: string; color: string; description?: string | null; } export interface ForgejoIssueMilestone { id: number | null; title: string; state: string; description: string | null; due_on: string | null; closed_at: string | null; } export interface ForgejoIssue { id: string; organization_id: string; repository_id: string; forgejo_issue_number: number; title: string; body: string | null; body_preview: string | null; state: string; is_pull_request: boolean; labels: ForgejoIssueLabel[]; assignees: Record[]; milestone: ForgejoIssueMilestone | null; author: string; html_url: string; forgejo_created_at: string; forgejo_updated_at: string; forgejo_closed_at: string | null; last_synced_at: string; created_at: string; updated_at: string; } export interface ForgejoIssueListResponse { items: ForgejoIssue[]; total: number; page: number; limit: number; } // Forgejo Issue API export async function getForgejoIssues(params?: { repository_id?: string; board_id?: string; state?: string; search?: string; page?: number; limit?: number; }): Promise { const searchParams = new URLSearchParams(); if (params?.repository_id) searchParams.set("repository_id", params.repository_id); if (params?.board_id) searchParams.set("board_id", params.board_id); if (params?.state) searchParams.set("state", params.state); if (params?.search) searchParams.set("search", params.search); if (params?.page) searchParams.set("page", params.page.toString()); if (params?.limit) searchParams.set("limit", params.limit.toString()); const qs = searchParams.toString(); return fetchJson( `/api/v1/forgejo/issues${qs ? `?${qs}` : ""}`, ); } export async function getForgejoIssue(issueId: string): Promise { return fetchJson(`/api/v1/forgejo/issues/${issueId}`); } export async function closeForgejoIssue( issueId: string, ): Promise { return fetchJson(`/api/v1/forgejo/issues/${issueId}/close`, { method: "POST", }); } // Board Repository Linking API export interface BoardForgejoRepositoryLink { id: string; board_id: string; repository_id: string; organization_id: string; created_at: string; repository?: ForgejoRepository; } export type BoardForgejoRepositoriesResponse = | BoardForgejoRepositoryLink[] | { repositories: BoardForgejoRepositoryLink[] }; export async function getBoardForgejoRepositories( boardId: string, ): Promise { return fetchJson( `/api/v1/boards/${boardId}/forgejo/repositories`, ); } export async function linkBoardForgejoRepository( boardId: string, repositoryId: string, ): Promise { return fetchJson< BoardForgejoRepositoryLink | { link?: BoardForgejoRepositoryLink } >(`/api/v1/boards/${boardId}/forgejo/repositories`, { method: "POST", body: JSON.stringify({ repository_id: repositoryId }), }); } export async function unlinkBoardForgejoRepository( boardId: string, repositoryId: string, ): Promise { await customFetch>( `/api/v1/boards/${boardId}/forgejo/repositories/${repositoryId}`, { method: "DELETE", }, ); } // Forgejo Metrics types export interface RepositorySyncHealth { repository_id: string; owner: string; repo: string; display_name: string | null; last_sync_at: string | null; last_sync_error: string | null; has_error: boolean; } export interface ForgejoIssueMetrics { open_issues: number; closed_issues: number; closed_last_7_days: number; closed_last_30_days: number; stale_open_issues: number; repositories_synced: number; last_sync_timestamps: Record; sync_error_counts: Record; } // Forgejo Heatmap API export interface ForgejoHeatmapDay { date: string; // "YYYY-MM-DD" count: number; } export interface ForgejoHeatmapResponse { days: ForgejoHeatmapDay[]; max_count: number; } export async function getForgejoHeatmap(params?: { organization_id?: string; }): Promise { const searchParams = new URLSearchParams(); if (params?.organization_id) searchParams.set("organization_id", params.organization_id); const qs = searchParams.toString(); return fetchJson( `/api/v1/forgejo/heatmap${qs ? `?${qs}` : ""}`, ); } // Forgejo Metrics API export async function getForgejoMetrics(params?: { organization_id?: string; board_id?: string; repository_id?: string; }): Promise { const searchParams = new URLSearchParams(); if (params?.organization_id) searchParams.set("organization_id", params.organization_id); if (params?.board_id) searchParams.set("board_id", params.board_id); if (params?.repository_id) searchParams.set("repository_id", params.repository_id); const qs = searchParams.toString(); return fetchJson( `/api/v1/forgejo/metrics${qs ? `?${qs}` : ""}`, ); }