From a7afdc73e8cd0c5843257a5a6f60c74ffe575d87 Mon Sep 17 00:00:00 2001 From: Marc Bernard <59966492+mbtools@users.noreply.github.com> Date: Sat, 21 Dec 2024 04:03:46 -0500 Subject: [PATCH] feat: show version-specific readmes in web ui (#4977) --- .changeset/strange-pants-chew.md | 5 ++++ packages/web/src/api/readme.ts | 27 +++++++++++++++-- packages/web/test/api.readme.test.ts | 29 +++++++++++++++++++ .../web/test/config/keep-all-readmes.yaml | 29 +++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 .changeset/strange-pants-chew.md create mode 100644 packages/web/test/config/keep-all-readmes.yaml diff --git a/.changeset/strange-pants-chew.md b/.changeset/strange-pants-chew.md new file mode 100644 index 000000000..dfd9f4d04 --- /dev/null +++ b/.changeset/strange-pants-chew.md @@ -0,0 +1,5 @@ +--- +'@verdaccio/web': patch +--- + +feat: show version-specific readmes in web ui diff --git a/packages/web/src/api/readme.ts b/packages/web/src/api/readme.ts index 1156431cb..529280b79 100644 --- a/packages/web/src/api/readme.ts +++ b/packages/web/src/api/readme.ts @@ -2,13 +2,14 @@ import buildDebug from 'debug'; import { Router } from 'express'; import { Auth } from '@verdaccio/auth'; -import { HEADERS, HEADER_TYPE } from '@verdaccio/core'; +import { DIST_TAGS, HEADERS, HEADER_TYPE } from '@verdaccio/core'; import { logger } from '@verdaccio/logger'; import { $NextFunctionVer, $RequestExtend, $ResponseExtend, allow } from '@verdaccio/middleware'; // Was required by other packages import { WebUrls } from '@verdaccio/middleware'; import { Storage } from '@verdaccio/store'; import { Manifest } from '@verdaccio/types'; +import { isVersionValid } from '@verdaccio/utils'; import { AuthorAvatar, addScope } from '../web-utils'; @@ -18,6 +19,7 @@ export { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by export type PackageExt = Manifest & { author: AuthorAvatar; dist?: { tarball: string } }; export const NOT_README_FOUND = 'ERROR: No README data found!'; const debug = buildDebug('verdaccio:web:api:readme'); + const getReadme = (readme) => { if (typeof readme === 'string' && readme.length === 0) { return NOT_README_FOUND; @@ -29,6 +31,25 @@ const getReadme = (readme) => { } }; +const getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => { + let id; + let readme; + if (typeof v === 'string' && isVersionValid(manifest, v)) { + id = 'version'; + readme = manifest.versions[v].readme; + } + if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) { + id = 'latest'; + readme = manifest.versions[manifest[DIST_TAGS].latest].readme; + } + if (!readme && manifest.readme) { + id = 'root'; + readme = manifest.readme; + } + debug('readme: %o %o', v, id); + return readme; +}; + function addReadmeWebApi(storage: Storage, auth: Auth): Router { debug('initialized readme web api'); const can = allow(auth, { @@ -66,7 +87,9 @@ function addReadmeWebApi(storage: Storage, auth: Auth): Router { })) as Manifest; debug('readme pkg %o', manifest?.name); res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8); - next(getReadme(manifest.readme)); + const { v } = req.query; + const readme = getReadmeFromManifest(manifest, v); + next(getReadme(readme)); } catch (err) { next(err); } diff --git a/packages/web/test/api.readme.test.ts b/packages/web/test/api.readme.test.ts index 4cfd1b8e6..e6d9f4585 100644 --- a/packages/web/test/api.readme.test.ts +++ b/packages/web/test/api.readme.test.ts @@ -73,4 +73,33 @@ describe('readme api', () => { .expect(HTTP_STATUS.OK); expect(response.text).toMatch(NOT_README_FOUND); }); + + test('should fetch readme with keeping all readmes (latest)', async () => { + const app = await initializeServer('keep-all-readmes.yaml'); + await publishVersion(app, 'pk1-test', '1.0.0', { readme: 'my readme' }); + const response = await supertest(app) + .get('/-/verdaccio/data/package/readme/pk1-test') + .set('Accept', HEADERS.TEXT_PLAIN) + .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8) + .expect(HTTP_STATUS.OK); + expect(response.text).toMatch('my readme'); + }); + + test('should fetch readme with keeping all readmes (version)', async () => { + const app = await initializeServer('keep-all-readmes.yaml'); + await publishVersion(app, 'pk1-test', '1.0.0', { readme: 'my readme' }); + await publishVersion(app, 'pk1-test', '1.2.0', { readme: 'my new readme' }); + const response = await supertest(app) + .get('/-/verdaccio/data/package/readme/pk1-test') + .set('Accept', HEADERS.TEXT_PLAIN) + .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8) + .expect(HTTP_STATUS.OK); + expect(response.text).toMatch('my new readme'); + const response2 = await supertest(app) + .get('/-/verdaccio/data/package/readme/pk1-test?v=1.0.0') + .set('Accept', HEADERS.TEXT_PLAIN) + .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8) + .expect(HTTP_STATUS.OK); + expect(response2.text).toMatch('my readme'); + }); }); diff --git a/packages/web/test/config/keep-all-readmes.yaml b/packages/web/test/config/keep-all-readmes.yaml new file mode 100644 index 000000000..4253e852a --- /dev/null +++ b/packages/web/test/config/keep-all-readmes.yaml @@ -0,0 +1,29 @@ +auth: + auth-memory: + users: + test: + name: test + password: test + +web: + title: verdaccio + +publish: + allow_offline: false + keep_readmes: all + +uplinks: + +log: { type: stdout, format: pretty, level: trace } + +packages: + '@*/*': + access: $anonymous + publish: $anonymous + '**': + access: $anonymous + publish: $anonymous +_debug: true + +flags: + changePassword: true