kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import { HTTPException } from "hono/http-exception";
2import { getGithubApp } from "../../plugins/github/utils/github-app";
3
4async function listUserRepositories() {
5 const githubApp = getGithubApp();
6
7 if (!githubApp) {
8 throw new HTTPException(500, {
9 message: "GitHub app not configured",
10 });
11 }
12
13 try {
14 const { data: installations } =
15 await githubApp.octokit.rest.apps.listInstallations();
16
17 const allRepositories = [];
18 const installationsWithRepos = [];
19
20 for (const installation of installations) {
21 try {
22 const installationOctokit = await githubApp.getInstallationOctokit(
23 installation.id,
24 );
25
26 // Use pagination to fetch ALL repositories for this installation
27 const repos = await installationOctokit.paginate(
28 installationOctokit.rest.apps.listReposAccessibleToInstallation,
29 {
30 per_page: 100, // GitHub's maximum
31 },
32 );
33
34 // Store installation info with repository names for UI
35 installationsWithRepos.push({
36 id: installation.id,
37 account: installation.account
38 ? {
39 login: installation.account.login,
40 type: installation.account.type,
41 }
42 : null,
43 repositories: repos.map((repo) => repo.full_name),
44 });
45
46 const mappedRepos = repos.map((repo) => ({
47 id: repo.id,
48 name: repo.name,
49 full_name: repo.full_name,
50 private: repo.private,
51 owner: {
52 login: repo.owner.login,
53 avatar_url: repo.owner.avatar_url,
54 type: repo.owner.type,
55 },
56 description: repo.description,
57 html_url: repo.html_url,
58 permissions: repo.permissions
59 ? {
60 admin: repo.permissions.admin,
61 push: repo.permissions.push,
62 pull: repo.permissions.pull,
63 }
64 : undefined,
65 updated_at: repo.updated_at || new Date().toISOString(),
66 installation_id: installation.id,
67 }));
68
69 allRepositories.push(...mappedRepos);
70 } catch (error) {
71 console.warn(
72 `Failed to get repositories for installation ${installation.id}:`,
73 error,
74 );
75 // Still add installation info even if repo fetch fails
76 installationsWithRepos.push({
77 id: installation.id,
78 account: installation.account
79 ? {
80 login: installation.account.login,
81 type: installation.account.type,
82 }
83 : null,
84 repositories: [],
85 });
86 }
87 }
88
89 // Remove duplicates and sort by most recently updated
90 const uniqueRepositories = allRepositories
91 .filter(
92 (repo, index, self) =>
93 index === self.findIndex((r) => r.id === repo.id),
94 )
95 .sort(
96 (a, b) =>
97 new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(),
98 );
99
100 return {
101 repositories: uniqueRepositories,
102 installations: installationsWithRepos,
103 total: uniqueRepositories.length,
104 };
105 } catch (error) {
106 console.error("Failed to list user repositories:", error);
107 throw new HTTPException(500, {
108 message: "Failed to fetch repositories from GitHub",
109 });
110 }
111}
112
113export default listUserRepositories;