diff --git a/.changeset/itchy-mangos-wink.md b/.changeset/itchy-mangos-wink.md new file mode 100644 index 000000000..bbbfde7db --- /dev/null +++ b/.changeset/itchy-mangos-wink.md @@ -0,0 +1,6 @@ +--- +'@verdaccio/middleware': patch +'@verdaccio/url': patch +--- + +Improved TS types for renderHTML() and related functions (by @tobbe in #4605) diff --git a/packages/core/url/src/index.ts b/packages/core/url/src/index.ts index bec367dbe..e7dc79942 100644 --- a/packages/core/url/src/index.ts +++ b/packages/core/url/src/index.ts @@ -1,4 +1,5 @@ import buildDebug from 'debug'; +import type { IncomingHttpHeaders } from 'node:http'; import { URL } from 'url'; import validator from 'validator'; @@ -31,7 +32,7 @@ export function isHost(url: string = '', options = {}): boolean { /** * Detect running protocol (http or https) */ -export function getWebProtocol(headerProtocol: string | void, protocol: string): string { +export function getWebProtocol(headerProtocol: string | undefined, protocol: string): string { let returnProtocol; const [, defaultProtocol] = validProtocols; // HAProxy variant might return http,http with X-Forwarded-Proto @@ -101,7 +102,7 @@ export type RequestOptions = { /** * Request headers. */ - headers: { [key: string]: string }; + headers: IncomingHttpHeaders; remoteAddress?: string; /** * Logged username the request, usually after token verification. @@ -119,10 +120,13 @@ export function getPublicUrl(url_prefix: string = '', requestOptions: RequestOpt if (!isHost(host)) { throw new Error('invalid host'); } - const protoHeader = + + const protoHeader: string = process.env.VERDACCIO_FORWARDED_PROTO?.toLocaleLowerCase() ?? HEADERS.FORWARDED_PROTO.toLowerCase(); - const protocol = getWebProtocol(requestOptions.headers[protoHeader], requestOptions.protocol); + const forwardedProtocolHeaderValue = requestOptions.headers[protoHeader] as string | undefined; + + const protocol = getWebProtocol(forwardedProtocolHeaderValue, requestOptions.protocol); const combinedUrl = combineBaseUrl(protocol, host, url_prefix); debug('public url by request %o', combinedUrl); return combinedUrl; diff --git a/packages/middleware/src/middlewares/web/utils/renderHTML.ts b/packages/middleware/src/middlewares/web/utils/renderHTML.ts index a2f96c46a..a36b0f89a 100644 --- a/packages/middleware/src/middlewares/web/utils/renderHTML.ts +++ b/packages/middleware/src/middlewares/web/utils/renderHTML.ts @@ -1,4 +1,5 @@ import buildDebug from 'debug'; +import type { Response } from 'express'; import LRU from 'lru-cache'; import path from 'path'; import { URL } from 'url'; @@ -6,10 +7,12 @@ import { URL } from 'url'; import { WEB_TITLE } from '@verdaccio/config'; import { HEADERS } from '@verdaccio/core'; import { ConfigYaml, TemplateUIOptions } from '@verdaccio/types'; -import { isURLhasValidProtocol } from '@verdaccio/url'; -import { getPublicUrl } from '@verdaccio/url'; +import type { RequestOptions } from '@verdaccio/url'; +import { getPublicUrl, isURLhasValidProtocol } from '@verdaccio/url'; +import type { Manifest } from './manifest'; import renderTemplate from './template'; +import type { WebpackManifest } from './template'; import { hasLogin, validatePrimaryColor } from './web-utils'; const DEFAULT_LANGUAGE = 'es-US'; @@ -17,19 +20,20 @@ const cache = new LRU({ max: 500, ttl: 1000 * 60 * 60 }); const debug = buildDebug('verdaccio:web:render'); -const defaultManifestFiles = { +const defaultManifestFiles: Manifest = { js: ['runtime.js', 'vendors.js', 'main.js'], ico: 'favicon.ico', + css: [], }; -export function resolveLogo(config: ConfigYaml, req) { +export function resolveLogo(config: ConfigYaml, requestOptions: RequestOptions) { if (typeof config?.web?.logo !== 'string') { return ''; } const isLocalFile = config?.web?.logo && !isURLhasValidProtocol(config?.web?.logo); if (isLocalFile) { - return `${getPublicUrl(config?.url_prefix, req)}-/static/${path.basename(config?.web?.logo)}`; + return `${getPublicUrl(config?.url_prefix, requestOptions)}-/static/${path.basename(config?.web?.logo)}`; } else if (isURLhasValidProtocol(config?.web?.logo)) { return config?.web?.logo; } else { @@ -37,9 +41,15 @@ export function resolveLogo(config: ConfigYaml, req) { } } -export default function renderHTML(config: ConfigYaml, manifest, manifestFiles, req, res) { +export default function renderHTML( + config: ConfigYaml, + manifest: WebpackManifest, + manifestFiles: Manifest | null | undefined, + requestOptions: RequestOptions, + res: Response +) { const { url_prefix } = config; - const base = getPublicUrl(config?.url_prefix, req); + const base = getPublicUrl(config?.url_prefix, requestOptions); const basename = new URL(base).pathname; const language = config?.i18n?.web ?? DEFAULT_LANGUAGE; const hideDeprecatedVersions = config?.web?.hideDeprecatedVersions ?? false; @@ -51,7 +61,7 @@ export default function renderHTML(config: ConfigYaml, manifest, manifestFiles, const title = config?.web?.title ?? WEB_TITLE; const login = hasLogin(config); const scope = config?.web?.scope ?? ''; - const logo = resolveLogo(config, req); + const logo = resolveLogo(config, requestOptions); const pkgManagers = config?.web?.pkgManagers ?? ['yarn', 'pnpm', 'npm']; const version = res.locals.app_version ?? ''; const flags = {