@@ -23,16 +23,6 @@ export const API_TAGS = {
2323 PUBLIC : "public" ,
2424} as const ;
2525
26- const GITHUB_ORG_REPO = "fastrepl/hyprnote" ;
27-
28- interface StargazerCache {
29- data : { username : string ; avatar : string } [ ] ;
30- timestamp : number ;
31- }
32-
33- let stargazerCache : StargazerCache | null = null ;
34- const CACHE_TTL_MS = 1000 * 60 * 60 ;
35-
3626const HealthResponseSchema = z . object ( {
3727 status : z . string ( ) ,
3828} ) ;
@@ -346,118 +336,3 @@ routes.get(
346336 return listenSocketHandler ( c , next ) ;
347337 } ,
348338) ;
349-
350- const StargazersResponseSchema = z . object ( {
351- stargazers : z . array (
352- z . object ( {
353- username : z . string ( ) ,
354- avatar : z . string ( ) ,
355- } ) ,
356- ) ,
357- } ) ;
358-
359- routes . get (
360- "/stargazers" ,
361- describeRoute ( {
362- tags : [ API_TAGS . PUBLIC ] ,
363- summary : "Get GitHub stargazers" ,
364- description :
365- "Returns the most recent GitHub stargazers for the Hyprnote repository. Results are cached for 1 hour." ,
366- responses : {
367- 200 : {
368- description : "List of stargazers" ,
369- content : {
370- "application/json" : {
371- schema : resolver ( StargazersResponseSchema ) ,
372- } ,
373- } ,
374- } ,
375- 500 : {
376- description : "Failed to fetch stargazers from GitHub" ,
377- content : {
378- "application/json" : {
379- schema : resolver ( z . object ( { error : z . string ( ) } ) ) ,
380- } ,
381- } ,
382- } ,
383- } ,
384- } ) ,
385- async ( c ) => {
386- const now = Date . now ( ) ;
387-
388- if ( stargazerCache && now - stargazerCache . timestamp < CACHE_TTL_MS ) {
389- return c . json ( { stargazers : stargazerCache . data } , 200 ) ;
390- }
391-
392- const githubToken = process . env . GITHUB_TOKEN ;
393- const githubHeaders : Record < string , string > = {
394- Accept : "application/vnd.github.v3+json" ,
395- "User-Agent" : "Hyprnote-API" ,
396- } ;
397- if ( githubToken ) {
398- githubHeaders [ "Authorization" ] = `token ${ githubToken } ` ;
399- }
400-
401- try {
402- const repoResponse = await fetch (
403- `https://api.github.com/repos/${ GITHUB_ORG_REPO } ` ,
404- { headers : githubHeaders } ,
405- ) ;
406-
407- if ( ! repoResponse . ok ) {
408- throw new Error ( `Failed to fetch repo info: ${ repoResponse . status } ` ) ;
409- }
410-
411- const repoData = await repoResponse . json ( ) ;
412- const totalStars = repoData . stargazers_count ?? 0 ;
413-
414- if ( totalStars === 0 ) {
415- return c . json ( { stargazers : [ ] } , 200 ) ;
416- }
417-
418- const count = 512 ;
419- const perPage = 100 ;
420- const numPages = Math . ceil ( Math . min ( count , totalStars ) / perPage ) ;
421- const lastPage = Math . ceil ( totalStars / perPage ) ;
422- const startPage = Math . max ( 1 , lastPage - numPages + 1 ) ;
423-
424- const fetchPromises = [ ] ;
425- for ( let page = startPage ; page <= lastPage ; page ++ ) {
426- fetchPromises . push (
427- fetch (
428- `https://api.github.com/repos/${ GITHUB_ORG_REPO } /stargazers?per_page=${ perPage } &page=${ page } ` ,
429- { headers : githubHeaders } ,
430- ) ,
431- ) ;
432- }
433-
434- const responses = await Promise . all ( fetchPromises ) ;
435- const allStargazers : { username : string ; avatar : string } [ ] = [ ] ;
436-
437- for ( const response of responses ) {
438- if ( ! response . ok ) continue ;
439- const data = await response . json ( ) ;
440- for ( const user of data ) {
441- allStargazers . push ( {
442- username : user . login ,
443- avatar : user . avatar_url ,
444- } ) ;
445- }
446- }
447-
448- const result = allStargazers . reverse ( ) . slice ( 0 , count ) ;
449-
450- stargazerCache = {
451- data : result ,
452- timestamp : now ,
453- } ;
454-
455- return c . json ( { stargazers : result } , 200 ) ;
456- } catch {
457- if ( stargazerCache ) {
458- return c . json ( { stargazers : stargazerCache . data } , 200 ) ;
459- }
460- return c . json ( { error : "Failed to fetch stargazers" } , 500 ) ;
461- }
462- } ,
463- ) ;
0 commit comments