1- import express from 'express'
2- import type { RequestHandler , Response } from 'express'
1+ import type { Response } from 'express'
32import type { ExtendedRequestWithPageInfo } from '../types'
43
54import type { ExtendedRequest , Page , Context , Permalink } from '@/types'
6- import statsd from '@/observability/lib/statsd.js'
7- import { defaultCacheControl } from '@/frame/middleware/cache-control.js'
8- import catchMiddlewareError from '@/observability/middleware/catch-middleware-error.js'
9- import {
10- SURROGATE_ENUMS ,
11- setFastlySurrogateKey ,
12- makeLanguageSurrogateKey ,
13- } from '@/frame/middleware/set-fastly-surrogate-key.js'
145import shortVersions from '@/versions/middleware/short-versions.js'
156import contextualize from '@/frame/middleware/context/context'
167import features from '@/versions/middleware/features.js'
178import { readCompressedJsonFile } from '@/frame/lib/read-json-file.js'
18- import { pathValidationMiddleware , pageValidationMiddleware } from './validation'
19-
20- const router = express . Router ( )
219
2210// If you have pre-computed page info into a JSON file on disk, this is
2311// where it would be expected to be found.
@@ -96,7 +84,7 @@ type CachedPageInfo = {
9684}
9785
9886let _cache : CachedPageInfo | null = null
99- async function getPageInfoFromCache ( page : Page , pathname : string ) {
87+ export async function getPageInfoFromCache ( page : Page , pathname : string ) {
10088 let cacheInfo = ''
10189 if ( _cache === null ) {
10290 try {
@@ -111,12 +99,12 @@ async function getPageInfoFromCache(page: Page, pathname: string) {
11199 }
112100 }
113101
114- let info = _cache [ pathname ]
102+ let meta = _cache [ pathname ]
115103 if ( ! cacheInfo ) {
116- cacheInfo = info ? 'hit' : 'miss'
104+ cacheInfo = meta ? 'hit' : 'miss'
117105 }
118- if ( ! info ) {
119- info = await getPageInfo ( page , pathname )
106+ if ( ! meta ) {
107+ meta = await getPageInfo ( page , pathname )
120108 // You might wonder; why do we not store this compute information
121109 // into the `_cache` from here?
122110 // The short answer is; it won't be used again.
@@ -128,74 +116,37 @@ async function getPageInfoFromCache(page: Page, pathname: string) {
128116 // In CI, we use the caching because the CI runs
129117 // `npm run precompute-pageinfo` right before it runs vitest tests.
130118 }
131- info . cacheInfo = cacheInfo
132- return info
119+ meta . cacheInfo = cacheInfo
120+ return meta
133121}
134122
135- router . get (
136- '/v1' ,
137- pathValidationMiddleware as RequestHandler ,
138- pageValidationMiddleware as RequestHandler ,
139- catchMiddlewareError ( async function pageInfo ( req : ExtendedRequestWithPageInfo , res : Response ) {
140- // Remember, the `validationMiddleware` will use redirects if the
141- // `pathname` used is a redirect (e.g. /en/articles/foo or
142- // /articles or '/en/enterprise-server@latest/foo/bar)
143- // So by the time we get here, the pathname should be one of the
144- // page's valid permalinks.
145- const { page, pathname, archived } = req . pageinfo
146-
147- if ( archived && archived . isArchived ) {
148- const { requestedVersion } = archived
149- const title = `GitHub Enterprise Server ${ requestedVersion } Help Documentation`
150- const intro = ''
151- const product = 'GitHub Enterprise Server'
152- defaultCacheControl ( res )
153- return res . json ( { info : { intro, title, product } } )
154- }
123+ export async function getMetadata ( req : ExtendedRequestWithPageInfo ) {
124+ // Remember, the `validationMiddleware` will use redirects if the
125+ // `pathname` used is a redirect (e.g. /en/articles/foo or
126+ // /articles or '/en/enterprise-server@latest/foo/bar)
127+ // So by the time we get here, the pathname should be one of the
128+ // page's valid permalinks.
129+ const { page, pathname, archived } = req . pageinfo
130+
131+ if ( archived && archived . isArchived ) {
132+ const { requestedVersion } = archived
133+ const title = `GitHub Enterprise Server ${ requestedVersion } Help Documentation`
134+ const intro = ''
135+ const product = 'GitHub Enterprise Server'
136+ return { meta : { intro, title, product } }
137+ }
155138
156- if ( ! page ) {
157- return res . status ( 400 ) . json ( { error : `No page found for '${ pathname } '` } )
158- }
139+ if ( ! page ) {
140+ throw new Error ( `No page found for '${ pathname } '` )
141+ }
159142
160- const pagePermalinks = page . permalinks . map ( ( p : Permalink ) => p . href )
161- if ( ! pagePermalinks . includes ( pathname ) ) {
162- throw new Error ( `pathname '${ pathname } ' not one of the page's permalinks` )
163- }
143+ const pagePermalinks = page . permalinks . map ( ( p : Permalink ) => p . href )
144+ if ( ! pagePermalinks . includes ( pathname ) ) {
145+ throw new Error ( `pathname '${ pathname } ' not one of the page's permalinks` )
146+ }
164147
165- const fromCache = await getPageInfoFromCache ( page , pathname )
166- const { cacheInfo, ...info } = fromCache
167-
168- const tags = [
169- // According to https://docs.datadoghq.com/getting_started/tagging/#define-tags
170- // the max length of a tag is 200 characters. Most of ours are less than
171- // that but we truncate just to be safe.
172- `pathname:${ pathname } ` . slice ( 0 , 200 ) ,
173- `language:${ page . languageCode } ` ,
174- `cache:${ cacheInfo } ` ,
175- ]
176- statsd . increment ( 'pageinfo.lookup' , 1 , tags )
177-
178- defaultCacheControl ( res )
179-
180- // This is necessary so that the `Surrogate-Key` header is set with
181- // the correct language surrogate key bit. By default, it's set
182- // from the pathname but `/api/**` URLs don't have a language
183- // (other than the default 'en').
184- // We do this so that all of these URLs are cached in Fastly by language
185- // which we need for the staggered purge.
186-
187- setFastlySurrogateKey (
188- res ,
189- `${ SURROGATE_ENUMS . DEFAULT } ${ makeLanguageSurrogateKey ( page . languageCode ) } ` ,
190- true ,
191- )
192- res . status ( 200 ) . json ( { info } )
193- } ) ,
194- )
195-
196- // Alias for the latest version
197- router . get ( '/' , ( req , res ) => {
198- res . redirect ( 307 , req . originalUrl . replace ( '/pageinfo' , '/pageinfo/v1' ) )
199- } )
200-
201- export default router
148+ const fromCache = await getPageInfoFromCache ( page , pathname )
149+ const { cacheInfo, ...meta } = fromCache
150+
151+ return { meta, cacheInfo }
152+ }
0 commit comments