Skip to content

Commit b88cff6

Browse files
authored
chore: add logs (#397)
1 parent 8369ad8 commit b88cff6

File tree

1 file changed

+225
-40
lines changed

1 file changed

+225
-40
lines changed

packages/app/server/api/repo/search.get.ts

Lines changed: 225 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,66 +7,215 @@ const querySchema = z.object({
77
text: z.string(),
88
});
99

10+
interface SearchDebugInfo {
11+
startTime: string;
12+
endTime: string;
13+
totalElapsedMs: number;
14+
processedRepositories: number;
15+
matchesFound: number;
16+
averageProcessingTimePerRepo: number;
17+
repositoriesPerSecond: number;
18+
searchQuery: string;
19+
status: "completed" | "aborted" | "error";
20+
flowErrors: Array<{
21+
stage: string;
22+
error: string;
23+
timestamp: string;
24+
repositoryContext?: string;
25+
}>;
26+
flowStages: Array<{
27+
stage: string;
28+
timestamp: string;
29+
details?: string;
30+
}>;
31+
}
32+
1033
export default defineEventHandler(async (event) => {
1134
const request = toWebRequest(event);
1235
const signal = request.signal;
1336

37+
const flowErrors: SearchDebugInfo["flowErrors"] = [];
38+
const flowStages: SearchDebugInfo["flowStages"] = [];
39+
40+
const addFlowStage = (stage: string, details?: string) => {
41+
flowStages.push({
42+
stage,
43+
timestamp: new Date().toISOString(),
44+
details,
45+
});
46+
};
47+
48+
const addFlowError = (
49+
stage: string,
50+
error: string,
51+
repositoryContext?: string,
52+
) => {
53+
flowErrors.push({
54+
stage,
55+
error,
56+
timestamp: new Date().toISOString(),
57+
repositoryContext,
58+
});
59+
};
60+
1461
try {
62+
addFlowStage("query_validation", "Starting query validation");
63+
1564
const query = await getValidatedQuery(event, (data) =>
1665
querySchema.parse(data),
1766
);
18-
if (!query.text) return { nodes: [] };
1967

20-
const app = useOctokitApp(event);
68+
if (!query.text) {
69+
addFlowStage("early_return", "Empty query text");
70+
return { nodes: [], debug: null };
71+
}
72+
73+
addFlowStage("query_validated", `Query: "${query.text}"`);
74+
75+
let app;
76+
try {
77+
addFlowStage("octokit_init", "Initializing Octokit app");
78+
app = useOctokitApp(event);
79+
addFlowStage("octokit_ready", "Octokit app initialized successfully");
80+
} catch (err: any) {
81+
addFlowError("octokit_init", err.message);
82+
throw new Error(`Failed to initialize Octokit: ${err.message}`);
83+
}
84+
2185
const searchText = query.text.toLowerCase();
2286
const matches: RepoNode[] = [];
87+
const startTime = Date.now();
88+
let processedRepositories = 0;
89+
let status: SearchDebugInfo["status"] = "completed";
90+
let skippedRepositories = 0;
91+
let suspendedErrors = 0;
2392

24-
await app.eachInstallation(async ({ octokit, installation }) => {
25-
if (signal.aborted) return;
26-
27-
if (installation.suspended_at) {
28-
console.warn(`Skipping suspended installation ${installation.id}`);
29-
return;
30-
}
93+
addFlowStage(
94+
"repository_iteration_start",
95+
`Starting to iterate repositories for search: "${searchText}"`,
96+
);
97+
98+
try {
99+
await app.eachRepository(async ({ repository }) => {
100+
try {
101+
if (signal.aborted) {
102+
addFlowStage(
103+
"search_aborted",
104+
`Aborted at repository: ${repository.full_name}`,
105+
);
106+
status = "aborted";
107+
return;
108+
}
31109

32-
try {
33-
const repos = await octokit.paginate("GET /installation/repositories");
110+
if (repository.private) {
111+
skippedRepositories++;
112+
return;
113+
}
34114

35-
for (const repository of repos) {
36-
if (signal.aborted) return;
37-
if (repository.private) continue;
115+
processedRepositories++;
116+
117+
// Add periodic progress tracking
118+
if (processedRepositories % 100 === 0) {
119+
const elapsed = Date.now() - startTime;
120+
addFlowStage(
121+
"progress_checkpoint",
122+
`Processed ${processedRepositories} repositories in ${elapsed}ms`,
123+
);
124+
}
38125

39126
const repoName = repository.name.toLowerCase();
40127
const ownerLogin = repository.owner.login.toLowerCase();
41128

42-
const nameScore = stringSimilarity.compareTwoStrings(
43-
repoName,
44-
searchText,
45-
);
46-
const ownerScore = stringSimilarity.compareTwoStrings(
47-
ownerLogin,
48-
searchText,
49-
);
129+
let nameScore, ownerScore;
130+
try {
131+
nameScore = stringSimilarity.compareTwoStrings(
132+
repoName,
133+
searchText,
134+
);
135+
ownerScore = stringSimilarity.compareTwoStrings(
136+
ownerLogin,
137+
searchText,
138+
);
139+
} catch (err: any) {
140+
addFlowError(
141+
"string_similarity",
142+
err.message,
143+
repository.full_name,
144+
);
145+
// Use fallback scoring
146+
nameScore = repoName.includes(searchText) ? 0.5 : 0;
147+
ownerScore = ownerLogin.includes(searchText) ? 0.5 : 0;
148+
}
50149

51-
matches.push({
52-
id: repository.id,
53-
name: repository.name,
54-
owner: {
55-
login: repository.owner.login,
56-
avatarUrl: repository.owner.avatar_url,
57-
},
58-
stars: repository.stargazers_count || 0,
59-
score: Math.max(nameScore, ownerScore),
60-
});
150+
try {
151+
matches.push({
152+
id: repository.id,
153+
name: repository.name,
154+
owner: {
155+
login: repository.owner.login,
156+
avatarUrl: repository.owner.avatar_url,
157+
},
158+
stars: repository.stargazers_count || 0,
159+
score: Math.max(nameScore, ownerScore),
160+
});
161+
} catch (err: any) {
162+
addFlowError("match_creation", err.message, repository.full_name);
163+
}
164+
} catch (err: any) {
165+
if (
166+
err.message?.includes("suspended") ||
167+
err.message?.includes("Installation")
168+
) {
169+
suspendedErrors++;
170+
addFlowError(
171+
"repository_suspended",
172+
err.message,
173+
repository.full_name,
174+
);
175+
return;
176+
}
177+
addFlowError(
178+
"repository_processing",
179+
err.message,
180+
repository.full_name,
181+
);
182+
throw err;
61183
}
62-
} catch (error) {
63-
console.warn(`Error fetching repositories for installation ${installation.id}:`, error);
184+
});
185+
186+
addFlowStage(
187+
"repository_iteration_complete",
188+
`Completed repository iteration`,
189+
);
190+
} catch (err: any) {
191+
if (
192+
err.message?.includes("suspended") ||
193+
err.message?.includes("Installation")
194+
) {
195+
addFlowError(
196+
"iteration_suspended",
197+
`Installation suspended after processing ${processedRepositories} repositories: ${err.message}`,
198+
);
199+
status = "completed";
200+
} else {
201+
addFlowError("iteration_failed", err.message);
202+
status = "error";
203+
throw err;
64204
}
65-
});
205+
}
66206

67-
matches.sort((a, b) =>
68-
b.score !== a.score ? b.score - a.score : b.stars - a.stars,
69-
);
207+
const totalElapsed = Date.now() - startTime;
208+
209+
addFlowStage("sorting_matches", `Sorting ${matches.length} matches`);
210+
211+
try {
212+
matches.sort((a, b) =>
213+
b.score !== a.score ? b.score - a.score : b.stars - a.stars,
214+
);
215+
addFlowStage("sorting_complete", "Matches sorted successfully");
216+
} catch (err: any) {
217+
addFlowError("sorting", err.message);
218+
}
70219

71220
const top = matches.slice(0, 10).map((node) => ({
72221
id: node.id,
@@ -75,8 +224,44 @@ export default defineEventHandler(async (event) => {
75224
stars: node.stars,
76225
}));
77226

78-
return { nodes: top };
227+
addFlowStage("response_preparation", `Prepared ${top.length} top results`);
228+
229+
const debugInfo: SearchDebugInfo = {
230+
startTime: new Date(startTime).toISOString(),
231+
endTime: new Date().toISOString(),
232+
totalElapsedMs: totalElapsed,
233+
processedRepositories,
234+
matchesFound: matches.length,
235+
averageProcessingTimePerRepo:
236+
processedRepositories > 0 ? totalElapsed / processedRepositories : 0,
237+
repositoriesPerSecond: processedRepositories / (totalElapsed / 1000),
238+
searchQuery: query.text,
239+
status,
240+
flowErrors,
241+
flowStages,
242+
};
243+
244+
return { nodes: top, debug: debugInfo };
79245
} catch (error) {
80-
return { nodes: [], error: true, message: (error as Error).message };
246+
addFlowError("global_error", (error as Error).message);
247+
248+
return {
249+
nodes: [],
250+
error: true,
251+
message: (error as Error).message,
252+
debug: {
253+
startTime: new Date().toISOString(),
254+
endTime: new Date().toISOString(),
255+
totalElapsedMs: 0,
256+
processedRepositories: 0,
257+
matchesFound: 0,
258+
averageProcessingTimePerRepo: 0,
259+
repositoriesPerSecond: 0,
260+
searchQuery: "",
261+
status: "error" as const,
262+
flowErrors,
263+
flowStages,
264+
},
265+
};
81266
}
82267
});

0 commit comments

Comments
 (0)