1
0
mirror of synced 2026-01-29 21:01:19 -05:00
Files
docs/src/article-api/middleware/pagelist.ts

113 lines
3.8 KiB
TypeScript

import express from 'express'
import type { Response, RequestHandler } from 'express'
import type { ExtendedRequest } from '@/types'
import { defaultCacheControl } from '@/frame/middleware/cache-control.js'
import { getProductStringFromPath, getVersionStringFromPath } from '#src/frame/lib/path-utils.js'
import { getLanguageCodeFromPath } from '#src/languages/middleware/detect-language.js'
import { pagelistValidationMiddleware } from './validation'
import catchMiddlewareError from '#src/observability/middleware/catch-middleware-error.js'
import statsd from '#src/observability/lib/statsd.js'
const router = express.Router()
// pagelistValidationMiddleware is used for every route to normalize the lang and version from the path
// If no version or lang is provided we'll assume english and fpt and redirect there
router.get(
'/',
pagelistValidationMiddleware as RequestHandler,
catchMiddlewareError(async function (req: ExtendedRequest, res: Response) {
res.redirect(
308,
req.originalUrl.replace(
'/pagelist',
`/pagelist/${req.context!.currentLanguage}/${req.context!.currentVersion}`,
),
)
}),
)
// handles paths with fragments that could be the language or the version
router.get(
'/:someParam',
pagelistValidationMiddleware as RequestHandler,
catchMiddlewareError(async function (req: ExtendedRequest, res: Response) {
const { someParam } = req.params
res.redirect(
308,
req.originalUrl.replace(
`/pagelist/${someParam}`,
`/pagelist/${req.context!.currentLanguage}/${req.context!.currentVersion}`,
),
)
}),
)
// for a fully qualified path with language and product version, we'll serve up the pagelist
router.get(
'/:lang/:productVersion',
pagelistValidationMiddleware as RequestHandler,
catchMiddlewareError(async function (req: ExtendedRequest, res: Response) {
if (!req.context || !req.context.pages) throw new Error('Request not contextualized.')
const pages = req.context.pages
// the keys of `context.pages` are permalinks
const keys = Object.keys(pages)
// we filter the permalinks to get only our target version and language
const filteredPermalinks = keys.filter((key) =>
versionMatcher(key, req.context!.currentVersion!, req.context!.currentLanguage!),
)
// if we've filtered it out of existence, there's no articles to return so we must've
// gotten a bad language or version
if (!filteredPermalinks.length) {
const { lang, productVersion } = req.params
res
.status(400)
.type('application/json')
.send(
JSON.stringify({
error: 'Invalid version or language code',
language: lang,
version: productVersion,
}),
)
return
}
incrementPagelistLookup(req.context!.currentVersion!, req.context!.currentLanguage!)
defaultCacheControl(res)
// new line added at the end so `wc` works as expected with `-l` and `-w`.
res.type('text').send(filteredPermalinks.join('\n').concat('\n'))
}),
)
function versionMatcher(key: string, targetVersion: string, targetLang: string) {
const versionFromPermalink = getVersionStringFromPath(key)
if (!versionFromPermalink) {
throw new Error(`Couldn't get version from the permalink ${key} when generating the pagelist.`)
}
if (getProductStringFromPath(key) === 'early-access') return null
const langFromPermalink = getLanguageCodeFromPath(key)
if (!langFromPermalink) {
throw new Error(`Couldn't get language from the permalink ${key} when generating the pagelist.`)
}
if (versionFromPermalink === targetVersion && langFromPermalink === targetLang) return key
}
function incrementPagelistLookup(version: string, language: string) {
const tags = [`version:${version}`, `language:${language}`]
statsd.increment('api.pagelist.lookup', 1, tags)
}
export default router