-
Notifications
You must be signed in to change notification settings - Fork 61.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add routes for
/article
, /article/body
, and /article/meta
(#54652)
- Loading branch information
1 parent
ce9bd49
commit 2f4c6a4
Showing
7 changed files
with
131 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import type { RequestHandler, Response } from 'express' | ||
import express from 'express' | ||
|
||
import { defaultCacheControl } from '@/frame/middleware/cache-control.js' | ||
import { Context } from '#src/types.js' | ||
import catchMiddlewareError from '@/observability/middleware/catch-middleware-error.js' | ||
import { ExtendedRequestWithPageInfo } from '../types' | ||
import { pageValidationMiddleware, pathValidationMiddleware } from './validation' | ||
import contextualize from '#src/frame/middleware/context/context.js' | ||
|
||
/** START helper functions */ | ||
|
||
// for now, we're just querying pageinfo, we'll likely replace /api/pageinfo | ||
// with /api/meta and move or reference that code here | ||
async function getArticleMetadata(req: ExtendedRequestWithPageInfo) { | ||
const queryString = new URLSearchParams(req.query as Record<string, string>).toString() | ||
const apiUrl = `${req.protocol}://${req.get('host')}/api/pageinfo${queryString ? `?${queryString}` : ''}` | ||
|
||
// Fetch the data from the pageinfo API | ||
const response = await fetch(apiUrl) | ||
|
||
// Check if the response is OK | ||
if (!response.ok) { | ||
const errorText = await response.text() | ||
throw new Error(`Failed to fetch metadata: ${response.status} ${errorText}`) | ||
} | ||
|
||
return await response.json() | ||
} | ||
|
||
async function getArticleBody(req: ExtendedRequestWithPageInfo) { | ||
// req.pageinfo is set from pageValidationMiddleware and pathValidationMiddleware | ||
// and is in the ExtendedRequestWithPageInfo | ||
const { page, pathname } = req.pageinfo | ||
|
||
// these parts allow us to render the page | ||
const mockedContext: Context = {} | ||
const renderingReq = { | ||
path: pathname, | ||
language: page.languageCode, | ||
pagePath: pathname, | ||
cookies: {}, | ||
context: mockedContext, | ||
headers: { | ||
'content-type': 'text/markdown', | ||
}, | ||
} | ||
|
||
// contextualize and render the page | ||
await contextualize(renderingReq as ExtendedRequestWithPageInfo, {} as Response, () => {}) | ||
renderingReq.context.page = page | ||
renderingReq.context.markdownRequested = true | ||
return await page.render(renderingReq.context) | ||
} | ||
|
||
/** END helper functions */ | ||
|
||
/** START routes */ | ||
const router = express.Router() | ||
|
||
// For all these routes: | ||
// - pathValidationMiddleware ensures the path is properly structured and handles errors when it's not | ||
// - pageValidationMiddleware fetches the page from the pagelist, returns 404 to the user if not found | ||
|
||
router.get( | ||
'/', | ||
pathValidationMiddleware as RequestHandler, | ||
pageValidationMiddleware as RequestHandler, | ||
catchMiddlewareError(async function (req: ExtendedRequestWithPageInfo, res: Response) { | ||
// First, fetch metadata | ||
const metaData = await getArticleMetadata(req) | ||
const bodyContent = await getArticleBody(req) | ||
|
||
defaultCacheControl(res) | ||
return res.json({ | ||
meta: metaData, | ||
body: bodyContent, | ||
}) | ||
}), | ||
) | ||
|
||
router.get( | ||
'/body', | ||
pathValidationMiddleware as RequestHandler, | ||
pageValidationMiddleware as RequestHandler, | ||
catchMiddlewareError(async function (req: ExtendedRequestWithPageInfo, res: Response) { | ||
const rendered = await getArticleBody(req) | ||
defaultCacheControl(res) | ||
return res.type('text/markdown').send(rendered) | ||
}), | ||
) | ||
|
||
router.get( | ||
'/meta', | ||
pathValidationMiddleware as RequestHandler, | ||
pageValidationMiddleware as RequestHandler, | ||
catchMiddlewareError(async function (req: ExtendedRequestWithPageInfo, res: Response) { | ||
const metaData = await getArticleMetadata(req) | ||
defaultCacheControl(res) | ||
return res.json(metaData) | ||
}), | ||
) | ||
|
||
/** END routes */ | ||
|
||
export default router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,7 +89,16 @@ export default function handleInvalidPaths( | |
// E.g. `/en/foo.md?bar=baz` | ||
const newUrl = req.originalUrl.replace(req.path, req.path.replace(/\/index\.md$/, '')) | ||
return res.redirect(newUrl) | ||
} else if (req.path.endsWith('.md')) { | ||
// encode the query params but also make them pretty so we can see | ||
// them as `/` and `@` in the address bar | ||
// e.g. /api/article/body?pathname=/en/[email protected]/admin... | ||
// NOT: /api/article/body?pathname=%2Fen%2Fenterprise-server%403.16%2Fadmin... | ||
const encodedPath = encodeURIComponent(req.path.replace(/\.md$/, '')) | ||
.replace(/%2F/g, '/') | ||
.replace(/%40/g, '@') | ||
const newUrl = `/api/article/body?pathname=${encodedPath}` | ||
return res.redirect(newUrl) | ||
} | ||
|
||
return next() | ||
} |