@@ -17,6 +17,63 @@ app.use(cors());
1717app . use ( express . static ( publicDir ) ) ;
1818app . use ( '/public' , express . static ( publicDir ) ) ;
1919
20+ const staticRoots = [ '/' , '/public' ] ;
21+
22+ type RouteInfo = {
23+ method : string ;
24+ path : string ;
25+ requiredParams ?: string [ ] ;
26+ optionalParams ?: string [ ] ;
27+ payload ?: string | null ;
28+ example ?: string ;
29+ } ;
30+
31+ const routeDocs : Record < string , Omit < RouteInfo , 'method' | 'path' > > = {
32+ 'GET /stats' : StatsController . routeDocs ,
33+ 'GET /languages' : LanguageController . routeDocs
34+ } ;
35+
36+ const getRoutes = ( ) : RouteInfo [ ] => {
37+ const routes : RouteInfo [ ] = [ ] ;
38+ const router = ( app as { _router ?: { stack ?: Array < { route ?: { path ?: string ; methods ?: Record < string , boolean > } } | { name ?: string ; handle ?: { stack ?: Array < { route ?: { path ?: string ; methods ?: Record < string , boolean > } } > } } > } } ) . _router ;
39+ const stack = router ?. stack ?? [ ] ;
40+
41+ for ( const layer of stack ) {
42+ if ( 'route' in layer && layer . route ) {
43+ const path = layer . route . path ?? '' ;
44+ const methods = Object . keys ( layer . route . methods ?? { } ) . filter ( ( method ) => layer . route ?. methods ?. [ method ] ) ;
45+ for ( const method of methods ) {
46+ const routeKey = `${ method . toUpperCase ( ) } ${ path } ` ;
47+ routes . push ( { method : method . toUpperCase ( ) , path, ...routeDocs [ routeKey ] } ) ;
48+ }
49+ } else if ( 'name' in layer && layer . name === 'router' && layer . handle ?. stack ) {
50+ for ( const nestedLayer of layer . handle . stack ) {
51+ if ( nestedLayer . route ) {
52+ const path = nestedLayer . route . path ?? '' ;
53+ const methods = Object . keys ( nestedLayer . route . methods ?? { } ) . filter ( ( method ) => nestedLayer . route ?. methods ?. [ method ] ) ;
54+ for ( const method of methods ) {
55+ const routeKey = `${ method . toUpperCase ( ) } ${ path } ` ;
56+ routes . push ( { method : method . toUpperCase ( ) , path, ...routeDocs [ routeKey ] } ) ;
57+ }
58+ }
59+ }
60+ }
61+ }
62+
63+ routes . sort ( ( a , b ) => a . path . localeCompare ( b . path ) || a . method . localeCompare ( b . method ) ) ;
64+ return routes ;
65+ } ;
66+
67+ app . get ( '/' , ( _req , res ) => {
68+ res . json ( {
69+ routes : getRoutes ( ) ,
70+ staticAssets : {
71+ roots : staticRoots ,
72+ example : '/sitemap.xml'
73+ }
74+ } ) ;
75+ } ) ;
76+
2077const PORT = process . env . PORT || 3000 ;
2178const APP_ENV = process . env . APP_ENV || 'development' ;
2279const PROTOCOL = APP_ENV === 'production' ? 'https' : 'http' ;
0 commit comments