feat!: remove fastify server packages (#5463)

This commit is contained in:
Juan Picado
2025-11-08 10:11:23 +01:00
committed by GitHub
parent 6d1a84aba1
commit f3d696facb
46 changed files with 310 additions and 3302 deletions

View File

@@ -10,7 +10,6 @@
'verdaccio-memory': major
'@verdaccio/search-indexer': major
'@verdaccio/server': major
'@verdaccio/server-fastify': major
'@verdaccio/logger': major
'verdaccio-audit': major
'@verdaccio/ui-components': major

View File

@@ -1,7 +1,6 @@
---
'@verdaccio/local-storage': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/ui-components': patch
'@verdaccio/tarball': patch

View File

@@ -5,7 +5,6 @@
'@verdaccio/local-scripts': patch
'@verdaccio/file-locking': patch
'@verdaccio/ui-theme': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/middleware': patch
'verdaccio': patch

View File

@@ -1,6 +1,5 @@
---
'@verdaccio/local-storage': patch
'@verdaccio/server-fastify': patch
'@verdaccio/middleware': patch
'@verdaccio/core': patch
'@verdaccio/config': patch

View File

@@ -11,7 +11,6 @@
'verdaccio-memory': patch
'@verdaccio/search-indexer': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/logger': patch
'@verdaccio/test-helper': patch
'@verdaccio/ui-components': patch

View File

@@ -41,7 +41,6 @@
"@verdaccio/search": "7.0.0",
"@verdaccio/search-indexer": "7.0.0",
"@verdaccio/server": "7.0.0",
"@verdaccio/server-fastify": "7.0.0",
"@verdaccio/signature": "7.0.0",
"@verdaccio/cli-standalone": "7.0.0",
"@verdaccio/store": "7.0.0",

View File

@@ -12,7 +12,6 @@
'verdaccio-memory': patch
'@verdaccio/search-indexer': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/logger': patch
'verdaccio-audit': patch
'@verdaccio/test-helper': patch

View File

@@ -4,7 +4,6 @@
'@verdaccio/ui-theme': patch
'@verdaccio/search-indexer': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/middleware': patch
'verdaccio': patch

View File

@@ -136,7 +136,6 @@ Any interaction with the server should be done through the port `8000` eg: `npm
#### Useful commands
- `pnpm debug`: Run the server in debug mode `--inspect`. UI runs too but without hot reload. For automatic break use `pnpm debug:break`.
- `pnpm debug:fastify`: To contribute on the [fastify migration](https://github.com/verdaccio/verdaccio/discussions/2155) this is a temporary command for such purpose.
- `pnpm website`: Build the website, for more commands to run the _website_, run `cd website` and then `pnpm serve`, website will run on port `3000`.
- `pnpm docker`: Build the docker image. Requires `docker` command available in your system.

View File

@@ -147,7 +147,6 @@
"_debug:reload": "nodemon -d 3 packages/verdaccio/debug/bootstrap.js",
"start:ts": "ts-node packages/verdaccio/src/start.ts -- --listen 8000",
"debug": "node --trace-warnings --trace-uncaught --inspect packages/verdaccio/debug/bootstrap.js",
"debug:fastify": "cross-env VERDACCIO_SERVER=fastify node --trace-warnings --trace-uncaught --inspect packages/verdaccio/debug/bootstrap.js",
"debug:break": "node --trace-warnings --trace-uncaught --inspect-brk packages/verdaccio/debug/bootstrap.js",
"changeset": "changeset",
"changeset:check": "changeset status --since-master",

View File

@@ -45,7 +45,6 @@
"@verdaccio/config": "workspace:8.0.0-next-8.24",
"@verdaccio/logger": "workspace:8.0.0-next-8.24",
"@verdaccio/server": "workspace:8.0.0-next-8.24",
"@verdaccio/server-fastify": "workspace:8.0.0-next-8.24",
"debug": "4.4.3",
"lodash": "4.17.21"
},

View File

@@ -10,7 +10,6 @@ import url from 'node:url';
import { getConfigParsed, getListenAddress } from '@verdaccio/config';
import { logger, setup } from '@verdaccio/logger';
import expressServer from '@verdaccio/server';
import fastifyServer from '@verdaccio/server-fastify';
import { ConfigYaml, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types';
import { displayExperimentsInfoBox } from './experiments';
@@ -123,60 +122,48 @@ export async function initServer(
const addr = getListenAddress(port ?? config?.listen, logger);
displayExperimentsInfoBox(config.flags);
let app;
if (process.env.VERDACCIO_SERVER === 'fastify') {
app = await fastifyServer(config);
app.listen({ port: addr.port, host: addr.host }, (err) => {
if (err) {
reject(err);
} else {
resolve();
let app = await expressServer(config);
const serverFactory = createServerFactory(config, addr, app);
serverFactory
.listen(addr.port || addr.path, addr.host, (): void => {
// send a message for test
if (isFunction(process.send)) {
process.send({
verdaccio_started: true,
});
}
const addressServer = `${
addr.path
? url.format({
protocol: 'unix',
pathname: addr.path,
})
: url.format({
protocol: addr.proto,
hostname: addr.host,
port: addr.port,
pathname: '/',
})
}`;
logger.info({ addressServer }, 'http address: @{addressServer}');
logger.info({ version }, 'version: @{version}');
resolve();
})
.on('error', function (err): void {
reject(err);
process.exitCode = 1;
});
} else {
app = await expressServer(config);
const serverFactory = createServerFactory(config, addr, app);
serverFactory
.listen(addr.port || addr.path, addr.host, (): void => {
// send a message for test
if (isFunction(process.send)) {
process.send({
verdaccio_started: true,
});
}
const addressServer = `${
addr.path
? url.format({
protocol: 'unix',
pathname: addr.path,
})
: url.format({
protocol: addr.proto,
hostname: addr.host,
port: addr.port,
pathname: '/',
})
}`;
logger.info({ addressServer }, 'http address: @{addressServer}');
logger.info({ version }, 'version: @{version}');
resolve();
})
.on('error', function (err): void {
reject(err);
process.exitCode = 1;
});
function handleShutdownGracefully() {
logger.info('received shutdown signal - closing server gracefully...');
serverFactory.close(() => {
logger.info('server closed.');
process.exit(0);
});
}
function handleShutdownGracefully() {
logger.info('received shutdown signal - closing server gracefully...');
serverFactory.close(() => {
logger.info('server closed.');
process.exit(0);
});
}
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
// Use once() so that receiving double signals exit the app.
process.once(signal, handleShutdownGracefully);
}
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
// Use once() so that receiving double signals exit the app.
process.once(signal, handleShutdownGracefully);
}
});
}

View File

@@ -15,9 +15,6 @@
},
{
"path": "../server/express"
},
{
"path": "../server/fastify"
}
]
}

View File

@@ -1,3 +0,0 @@
{
"extends": "../../../.babelrc"
}

View File

@@ -1,6 +0,0 @@
node_modules
coverage/
lib/
.nyc_output
tests-report/
build/

View File

@@ -1,5 +0,0 @@
{
"rules": {
"@typescript-eslint/no-use-before-define": "off"
}
}

View File

@@ -1 +0,0 @@
lib/

View File

@@ -1,6 +0,0 @@
/*
!/bin/**/*
!/build/**/*
!index.js
!LICENSE
!README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025 Verdaccio contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,88 +0,0 @@
# @verdaccio/server-fastify - Verdaccio Fastify Server
[![Verdaccio Home](https://img.shields.io/badge/Homepage-Verdaccio-405236?style=flat)](https://verdaccio.org)
[![MIT License](https://img.shields.io/github/license/verdaccio/verdaccio?label=License&color=405236)](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
[![Verdaccio Latest](https://img.shields.io/npm/v/verdaccio?label=Latest%20Version&color=405236)](https://github.com/verdaccio/verdaccio)
[![This Package Latest](https://img.shields.io/npm/v/@verdaccio/server-fastify?label=@verdaccio/server-fastify&color=405236)](https://npmjs.com/package/@verdaccio/server-fastify)
[![Documentation](https://img.shields.io/badge/Help-Verdaccio?style=flat&logo=Verdaccio&label=Verdaccio&color=cd4000)](https://verdaccio.org/docs)
[![Discord](https://img.shields.io/badge/Chat-Discord?style=flat&logo=Discord&label=Discord&color=cd4000)](https://discord.com/channels/388674437219745793)
[![Bluesky](https://img.shields.io/badge/Follow-Bluesky?style=flat&logo=Bluesky&label=Bluesky&color=cd4000)](https://bsky.app/profile/verdaccio.org)
[![Backers](https://img.shields.io/opencollective/backers/verdaccio?style=flat&logo=opencollective&label=Join%20Backers&color=cd4000)](https://opencollective.com/verdaccio/contribute)
[![Sponsors](https://img.shields.io/opencollective/sponsors/verdaccio?style=flat&logo=opencollective&label=Sponsor%20Us&color=cd4000)](https://opencollective.com/verdaccio/contribute)
[![Verdaccio Downloads](https://img.shields.io/npm/dm/verdaccio?style=flat&logo=npm&label=Npm%20Downloads&color=lightgrey)](https://www.npmjs.com/package/verdaccio)
[![Docker Pulls](https://img.shields.io/docker/pulls/verdaccio/verdaccio?style=flat&logo=docker&label=Docker%20Pulls&color=lightgrey)](https://hub.docker.com/r/verdaccio/verdaccio)
[![GitHub Stars](https://img.shields.io/github/stars/verdaccio?style=flat&logo=github&label=GitHub%20Stars%20%E2%AD%90&color=lightgrey)](https://github.com/verdaccio/verdaccio/stargazers)
A package intended to start a migration from Express to fastify. (WIP).
Run from root folder
```js
pnpm debug -- new
```
## Donations
Verdaccio is run by **volunteers**; nobody is working full-time on it. If you find this project to be useful and would like to support its development, consider making a donation - **your logo might end up in this readme.** 😉
**[Donate](https://opencollective.com/verdaccio)** 💵👍🏻 starting from _\$1/month_ or just one single contribution.
## Report a vulnerability
If you want to report a security vulnerability, please follow the steps which we have defined for you in our [security policy](https://github.com/verdaccio/verdaccio/security/policy).
## Open Collective Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/verdaccio/contribute)]
[![sponsor](https://opencollective.com/verdaccio/sponsor/0/avatar.svg)](https://opencollective.com/verdaccio/sponsor/0/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/1/avatar.svg)](https://opencollective.com/verdaccio/sponsor/1/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/2/avatar.svg)](https://opencollective.com/verdaccio/sponsor/2/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/3/avatar.svg)](https://opencollective.com/verdaccio/sponsor/3/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/4/avatar.svg)](https://opencollective.com/verdaccio/sponsor/4/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/5/avatar.svg)](https://opencollective.com/verdaccio/sponsor/5/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/6/avatar.svg)](https://opencollective.com/verdaccio/sponsor/6/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/7/avatar.svg)](https://opencollective.com/verdaccio/sponsor/7/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/8/avatar.svg)](https://opencollective.com/verdaccio/sponsor/8/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/9/avatar.svg)](https://opencollective.com/verdaccio/sponsor/9/website)
## Open Collective Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/verdaccio/contribute)]
[![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio/contributes)
## Special Thanks
Thanks to the following companies to help us to achieve our goals providing free open source licenses.
[![jetbrains](https://github.com/verdaccio/verdaccio/blob/master/assets/thanks/jetbrains/logo.jpg?raw=true)](https://www.jetbrains.com/)
[![crowdin](https://github.com/verdaccio/verdaccio/blob/master/assets/thanks/crowdin/logo.png?raw=true)](https://crowdin.com/)
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md)].
[![contributors](https://opencollective.com/verdaccio/contributors.svg?width=890&button=true)](https://github.com/verdaccio/verdaccio/graphs/contributors)
## FAQ / Contact / Troubleshoot
If you have any issue you can try the following options. Do not hesitate to ask or check our issues database. Perhaps someone has asked already what you are looking for.
- [Blog](https://verdaccio.org/blog/)
- [Donations](https://opencollective.com/verdaccio)
- [Reporting an issue](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md#reporting-a-bug)
- [Running discussions](https://github.com/orgs/verdaccio/discussions)
- [Chat](https://discord.com/channels/388674437219745793)
- [Logos](https://verdaccio.org/docs/logo)
- [Docker Examples](https://github.com/verdaccio/verdaccio/tree/master/docker-examples)
- [FAQ](https://github.com/verdaccio/verdaccio/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20)
## License
Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
The Verdaccio documentation and logos (excluding /thanks, e.g., .md, .png, .sketch files within the /assets folder) are
[Creative Commons licensed](https://creativecommons.org/licenses/by/4.0/).

View File

@@ -1,39 +0,0 @@
storage: ./storage
plugins: ./plugins
web:
title: Verdaccio
auth:
htpasswd:
file: ./htpasswd
uplinks:
npmjs:
url: https://registry.npmjs.org/
packages:
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
server:
keepAliveTimeout: 60
middlewares:
audit:
enabled: true
log: { type: stdout, format: pretty, level: http }
flags:
token: false
search: false
i18n:
web: en-US

View File

@@ -1,32 +0,0 @@
import buildDebug from 'debug';
import path from 'node:path';
import { parseConfigFile } from '@verdaccio/config';
import { logger, setup } from '@verdaccio/logger';
import server from '../src/index';
const debug = buildDebug('verdaccio:fastify:debug');
/**
* This file is intended for fast development and debug, it should
* be removed eventually and the app start from @verdaccio/cli package.
*/
(async () => {
try {
const configFile = path.join(__dirname, './fastify-conf.yaml');
debug('configFile %s', configFile);
const configParsed = parseConfigFile(configFile);
setup(configParsed.log);
logger.info(`config location ${configFile}`);
debug('configParsed %s', configParsed);
process.title = 'fastify-verdaccio';
const ser = await server({ logger, config: configParsed });
await ser.listen(4873);
logger.info('fastify running on port 4873');
} catch (err: any) {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
}
})();

View File

@@ -1,63 +0,0 @@
{
"name": "@verdaccio/server-fastify",
"version": "8.0.0-next-8.24",
"description": "Verdaccio Fastify Server",
"keywords": [
"private",
"package",
"repository",
"registry",
"enterprise",
"modules",
"proxy",
"server",
"verdaccio"
],
"main": "./build/index.js",
"types": "./build/index.d.ts",
"author": "Juan Picado <juanpicado19@gmail.com>",
"license": "MIT",
"homepage": "https://verdaccio.org",
"engines": {
"node": ">=18"
},
"repository": {
"type": "https",
"url": "https://github.com/verdaccio/verdaccio",
"directory": "packages/server/fastify"
},
"bugs": {
"url": "https://github.com/verdaccio/verdaccio/issues"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"@verdaccio/core": "workspace:8.0.0-next-8.24",
"@verdaccio/config": "workspace:8.0.0-next-8.24",
"@verdaccio/auth": "workspace:8.0.0-next-8.24",
"@verdaccio/logger": "workspace:8.0.0-next-8.24",
"@verdaccio/store": "workspace:8.0.0-next-8.24",
"debug": "4.4.3",
"fastify": "4.25.2",
"fastify-plugin": "4.5.1",
"lodash": "4.17.21"
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"ts-node": "10.9.2"
},
"scripts": {
"clean": "rimraf ./build",
"type-check": "tsc --noEmit -p tsconfig.build.json",
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
"watch": "pnpm build:js -- --watch",
"build": "pnpm run build:js && pnpm run build:types",
"start": "ts-node debug/index.ts"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/verdaccio"
}
}

View File

@@ -1,52 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { MergeTags } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:dist-tags');
interface ParamsInterface {
packageName: string;
}
async function distTagsRoute(fastify: FastifyInstance) {
fastify.get<{ Params: ParamsInterface }>(
'/-/package/:packageName/dist-tags',
async (request, reply) => {
// @ts-ignore
const { packageName } = request.params;
debug('dist-tags: response %o', packageName);
const requestOptions = {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
remoteAddress: request.socket.remoteAddress,
};
const manifest = fastify.storage.getPackageByOptions({
name: packageName,
uplinksLook: true,
keepUpLinkData: true,
requestOptions,
});
reply.code(fastify.statusCode.OK).send(manifest[fastify.constants.DIST_TAGS]);
}
);
fastify.post<{ Params: ParamsInterface; Body: MergeTags }>(
'/-/package/:packageName/dist-tags',
async (request) => {
const { packageName } = request.params;
await fastify.storage.mergeTagsNext(packageName, request.body);
return { ok: fastify.constants.API_MESSAGE.TAG_UPDATED };
}
);
fastify.delete('/-/package/:packageName/dist-tags', async (request, reply) => {
// @ts-ignore
// const { packageName } = request.params;
reply.code(fastify.statusCode.NOT_FOUND);
});
}
export default distTagsRoute;

View File

@@ -1,68 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { stringUtils } from '@verdaccio/core';
import { Storage } from '@verdaccio/store';
import { Package, Version } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:api:sidebar');
export type $SidebarPackage = Package & { latest: Version };
interface ParamsInterface {
name: string;
version: string;
}
async function manifestRoute(fastify: FastifyInstance) {
fastify.get<{ Params: ParamsInterface }>('/:name', async (request) => {
const { name } = request.params;
const storage = fastify.storage;
debug('pkg name %s ', name);
// @ts-ignore
const abbreviated =
stringUtils.getByQualityPriorityValue(request.headers['accept']) ===
Storage.ABBREVIATED_HEADER;
const data = await storage?.getPackageByOptions({
name,
// @ts-ignore
uplinksLook: true,
requestOptions: {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
},
abbreviated,
});
return data;
});
interface QueryInterface {
write: string;
}
fastify.get<{ Params: ParamsInterface; Querystring: QueryInterface }>(
'/:packageName/:version',
async (request) => {
const { name, version } = request.params;
const storage = fastify.storage;
const write = request.query.write === 'true';
debug('pkg name %s, with version / tag: %s ', name, version);
const requestOptions = {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
remoteAddress: request.socket.remoteAddress,
byPassCache: write,
};
const data = await storage?.getPackageByOptions({
name,
version,
uplinksLook: true,
requestOptions,
});
return data;
}
);
}
export default manifestRoute;

View File

@@ -1,15 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-invalid-this */
import { FastifyInstance } from 'fastify';
import { logger } from '@verdaccio/logger';
async function pingRoute(fastify: FastifyInstance) {
fastify.get('/-/ping', async () => {
logger.http('ping');
return {};
});
}
export default pingRoute;

View File

@@ -1,30 +0,0 @@
// import buildDebug from 'debug';
// import { FastifyInstance } from 'fastify';
// import { Package, Version } from '@verdaccio/types';
// const debug = buildDebug('verdaccio:web:api:sidebar');
// export type $SidebarPackage = Package & { latest: Version };
// async function manifestRoute(fastify: FastifyInstance) {
// // TODO: review // :_rev?/:revision?
// fastify.put('/:packageName', async (request) => {
// // @ts-ignore
// const { packageName } = request.params;
// const storage = fastify.storage;
// debug('pkg name %s ', packageName);
// // const data = await storage?.getPackageNext({
// // name: packageName,
// // req: request.raw,
// // uplinksLook: true,
// // requestOptions: {
// // protocol: request.protocol,
// // headers: request.headers as any,
// // host: request.hostname,
// // },
// // });
// // return data;
// });
// }
// export default manifestRoute;

View File

@@ -1,37 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-invalid-this */
import { FastifyInstance } from 'fastify';
import { searchUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
interface QueryInterface {
url: string;
query: searchUtils.SearchQuery;
}
async function searchRoute(fastify: FastifyInstance) {
fastify.get<{ Querystring: QueryInterface }>('/-/v1/search', async (request, reply) => {
// TODO: apply security layer here like in
// packages/api/src/v1/search.ts
// TODO: add validations for query, some parameters are mandatory
// TODO: review which query fields are mandatory
const abort = new AbortController();
request.socket.on('aborted', () => {
abort.abort();
});
const { url, query } = request.query;
const storage = fastify.storage;
const data = await storage.search({
query,
url,
abort,
});
logger.http('search endpoint');
reply.code(200).send(data);
});
}
export default searchRoute;

View File

@@ -1,69 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { HEADERS, HEADER_TYPE } from '@verdaccio/core';
const debug = buildDebug('verdaccio:fastify:tarball');
interface ParamsInterface {
package: string;
filename: string;
}
async function tarballRoute(fastify: FastifyInstance) {
fastify.get<{ Params: ParamsInterface }>('/:package/-/:filename', async (request, reply) => {
const { package: pkg, filename } = request.params;
debug('stream tarball for %s@%s', pkg, filename);
const abort = new AbortController();
const stream = (await fastify.storage.getTarball(pkg, filename, {
signal: abort.signal,
// enableRemote: true,
})) as any;
stream.on('content-length', (size: number) => {
reply.header(HEADER_TYPE.CONTENT_LENGTH, size);
});
// request.socket.on('abort', () => {
// debug('request aborted for %o', request.url);
// abort.abort();
// });
return stream;
});
interface ScopeParamsInterface {
filename: string;
scope: string;
name: string;
}
fastify.get<{ Params: ScopeParamsInterface }>(
'/:scope/:name/-/:filename',
async (request, reply) => {
const abort = new AbortController();
const { scope, name, filename } = request.params;
const scopedPackage = `${scope}/${name}`;
debug('stream scope tarball for %s@%s', scopedPackage, filename);
const stream = (await fastify.storage.getTarball(scopedPackage, filename, {
signal: abort.signal,
// enableRemote: true,
})) as any;
stream.on('content-length', (size: number) => {
reply.header(HEADER_TYPE.CONTENT_LENGTH, size);
});
// request.socket.on('abort', () => {
// debug('request aborted for %o', request.url);
// abort.abort();
// });
reply.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);
return stream;
}
);
}
export default tarballRoute;

View File

@@ -1,151 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-invalid-this */
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import _ from 'lodash';
import { getApiToken } from '@verdaccio/auth';
import { createRemoteUser } from '@verdaccio/config';
import { authUtils, validationUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
import { RemoteUser } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:user');
async function userRoute(fastify: FastifyInstance) {
interface UserParamsInterface {
org_couchdb_user: string;
}
fastify.get<{ Params: UserParamsInterface }>('/:org_couchdb_user', async (request, reply) => {
// @ts-ignore
// TODO: compare org_couchdb_user with remote user name
const message = authUtils.getAuthenticatedMessage(request.userRemote.name);
logger.info('user authenticated message %o', message);
reply.code(fastify.statusCode.OK);
return { ok: message };
});
interface DeleteTokenParamsInterface {
token: string;
}
fastify.delete<{ Params: DeleteTokenParamsInterface }>(
'/token/:token',
async (request, reply) => {
debug('loging out');
const { token } = request.params;
const userRemote: RemoteUser = request.userRemote;
await fastify.auth.invalidateToken(token);
console.log('userRoute', userRemote);
reply.code(fastify.statusCode.OK);
return { ok: fastify.apiMessage.LOGGED_OUT };
}
);
interface UpdateUserParamsInterface {
username: string;
}
fastify.put<{
Body: { name: string; password: string };
Params: UpdateUserParamsInterface;
}>('/:username', async (request, reply) => {
const { name, password } = request.body;
const remoteName = request.userRemote.name;
if (_.isNil(remoteName) === false && _.isNil(name) === false && remoteName === name) {
// debug('login: no remote user detected');
fastify.auth.authenticate(
name,
password,
async function callbackAuthenticate(err, user): Promise<void> {
if (err) {
logger.trace(
{ name, err },
'authenticating for user @{username} failed. Error: @{err.message}'
);
reply
.code(fastify.statusCode.UNAUTHORIZED)
.send(
fastify.errorUtils.getCode(
fastify.statusCode.UNAUTHORIZED,
fastify.apiError.BAD_USERNAME_PASSWORD
)
);
}
const restoredRemoteUser: RemoteUser = createRemoteUser(name, user?.groups || []);
const token = await getApiToken(
fastify.auth,
fastify.configInstance,
restoredRemoteUser,
password
);
debug('login: new token');
if (!token) {
return reply.send(fastify.errorUtils.getUnauthorized());
} else {
reply.code(fastify.statusCode.CREATED);
const message = authUtils.getAuthenticatedMessage(remoteName);
debug('login: created user message %o', message);
reply.send({
ok: message,
token,
});
}
}
);
} else {
if (
validationUtils.validatePassword(
password as string,
fastify.configInstance?.server?.passwordValidationRegex
) === false
) {
debug('adduser: invalid password');
reply.code(fastify.statusCode.BAD_REQUEST).send(
fastify.errorUtils.getCode(
fastify.statusCode.BAD_REQUEST,
// eslint-disable-next-line new-cap
fastify.apiError.PASSWORD_SHORT
)
);
return;
}
fastify.auth.add_user(name, password, async function (err, user): Promise<void> {
if (err) {
if (
err.status >= fastify.statusCode.BAD_REQUEST &&
err.status < fastify.statusCode.INTERNAL_ERROR
) {
debug('adduser: error on create user');
// With npm registering is the same as logging in,
// and npm accepts only an 409 error.
// So, changing status code here.
const addUserError =
fastify.errorUtils.getCode(err.status, err.message) ||
fastify.errorUtils.getConflict(err.message);
reply.send(addUserError);
return;
}
}
const token =
name && password
? await getApiToken(fastify.auth, fastify.configInstance, user as RemoteUser, password)
: undefined;
debug('adduser: new token %o', token);
if (!token) {
return reply.send(fastify.errorUtils.getUnauthorized());
}
debug('adduser: user has been created');
reply.code(fastify.statusCode.CREATED).send({
ok: `user '${name}' created`,
token,
});
});
}
});
}
export default userRoute;

View File

@@ -1,15 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
const debug = buildDebug('verdaccio:fastify:whoami');
async function whoamiRoute(fastify: FastifyInstance) {
fastify.get('/-/whoami', async (request, reply) => {
const username: string | void = request.userRemote.name;
debug('whoami: response %o', username);
reply.code(fastify.statusCode.OK);
return { username };
});
}
export default whoamiRoute;

View File

@@ -1 +0,0 @@
export { default } from './server';

View File

@@ -1,24 +0,0 @@
import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin';
import { Auth } from '@verdaccio/auth';
import { logger } from '@verdaccio/logger';
import { Config as IConfig } from '@verdaccio/types';
export default fp(
async function (fastify: FastifyInstance, opts: { config: IConfig; filters?: unknown }) {
const { config } = opts;
const auth = new Auth(config, logger);
await auth.init();
fastify.decorate('auth', auth);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
auth: Auth;
}
}

View File

@@ -1,22 +0,0 @@
import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin';
import { Config as AppConfig } from '@verdaccio/config';
import { ConfigYaml, Config as IConfig } from '@verdaccio/types';
export default fp(
async function (fastify: FastifyInstance, opts: { config: ConfigYaml }) {
const { config } = opts;
const configInstance: IConfig = new AppConfig(Object.assign({}, config) as any);
fastify.decorate('configInstance', configInstance);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
configInstance: IConfig;
}
}

View File

@@ -1,47 +0,0 @@
import fp from 'fastify-plugin';
import {
API_ERROR,
API_MESSAGE,
HTTP_STATUS,
constants,
errorUtils,
pluginUtils,
searchUtils,
streamUtils,
validationUtils,
warningUtils,
} from '@verdaccio/core';
export default fp(
async function (fastify) {
fastify.decorate('errorUtils', errorUtils);
fastify.decorate('searchUtils', searchUtils);
fastify.decorate('streamUtils', streamUtils);
fastify.decorate('validationUtils', validationUtils);
fastify.decorate('pluginUtils', pluginUtils);
fastify.decorate('warningUtils', warningUtils);
fastify.decorate('apiError', API_ERROR);
fastify.decorate('constants', constants);
fastify.decorate('apiMessage', API_MESSAGE);
fastify.decorate('statusCode', HTTP_STATUS);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
apiError: typeof API_ERROR;
apiMessage: typeof API_MESSAGE;
statusCode: typeof HTTP_STATUS;
errorUtils: typeof errorUtils;
warningUtils: typeof warningUtils;
searchUtils: typeof searchUtils;
streamUtils: typeof streamUtils;
pluginUtils: typeof pluginUtils;
validationUtils: typeof validationUtils;
constants: typeof constants;
}
}

View File

@@ -1,25 +0,0 @@
import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin';
import { logger } from '@verdaccio/logger';
import { Storage } from '@verdaccio/store';
import { Config as IConfig } from '@verdaccio/types';
export default fp(
async function (fastify: FastifyInstance, opts: { config: IConfig; filters?: unknown }) {
const { config } = opts;
const storage: Storage = new Storage(config, logger);
// @ts-ignore
await storage.init(config, []);
fastify.decorate('storage', storage);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
storage: Storage;
}
}

View File

@@ -1,115 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import _ from 'lodash';
import { validationUtils } from '@verdaccio/core';
import { JWTSignOptions, RemoteUser } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:web:login');
const loginBodySchema = {
body: {
type: 'object',
required: ['username', 'password'],
additionalProperties: false,
properties: {
username: { type: 'string' },
password: { type: 'string' },
},
},
};
const resetPasswordSchema = {
body: {
type: 'object',
required: ['password'],
additionalProperties: false,
properties: {
password: { type: 'string' },
},
},
};
async function loginRoute(fastify: FastifyInstance) {
fastify.post(
'/login',
{
schema: loginBodySchema,
},
async (request, reply) => {
// @ts-expect-error
const { username, password } = request.body;
debug('authenticate %o', username);
fastify.auth.authenticate(
username,
password,
async function callbackAuthenticate(err, user): Promise<void> {
if (err) {
const errorCode = err.message
? fastify.statusCode.UNAUTHORIZED
: fastify.statusCode.INTERNAL_ERROR;
reply.send(fastify.errorUtils.getCode(errorCode, err.message));
} else {
const jWTSignOptions: JWTSignOptions = fastify.configInstance.security.web.sign;
debug('jwtSignOptions: %o', jWTSignOptions);
const token = await fastify.auth.jwtEncrypt(user as RemoteUser, jWTSignOptions);
reply.code(fastify.statusCode.OK).send({ token, username });
}
}
);
}
);
fastify.put(
'/reset_password',
{
schema: resetPasswordSchema,
},
async (request, reply) => {
if (_.isNil(request.userRemote.name)) {
reply.send(
fastify.errorUtils.getCode(
fastify.statusCode.UNAUTHORIZED,
fastify.errorUtils.API_ERROR.MUST_BE_LOGGED
)
);
}
// @ts-ignore
const { password } = request.body;
const { name } = request.userRemote;
if (
validationUtils.validatePassword(
password.new,
fastify.configInstance?.server?.passwordValidationRegex
) === false
) {
reply.send(
fastify.errorUtils.getCode(
fastify.statusCode.BAD_REQUEST,
fastify.errorUtils.APP_ERROR.PASSWORD_VALIDATION
)
);
return;
}
fastify.auth.changePassword(
name as string,
password.old,
password.new,
(err, isUpdated): void => {
if (_.isNil(err) && isUpdated) {
reply.code(fastify.statusCode.OK);
} else {
reply.send(
fastify.errorUtils.getInternalError(
fastify.errorUtils.API_ERROR.INTERNAL_SERVER_ERROR
)
);
}
}
);
}
);
// });
}
export default loginRoute;

View File

@@ -1,60 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { Manifest } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:web:readme');
export const NOT_README_FOUND = 'ERROR: No README data found!';
async function readmeRoute(fastify: FastifyInstance) {
fastify.get('/package/readme/:packageName', async (request, reply) => {
// @ts-ignore
const { version, packageName } = request.params;
debug('readme name %s version: %s', packageName, version);
const manifest = (await fastify.storage?.getPackageByOptions({
name: packageName,
// remove on refactor getPackageByOptions
// @ts-ignore
req: request.raw,
version,
uplinksLook: true,
requestOptions: {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
},
})) as Manifest;
try {
const parsedReadme = manifest.readme;
reply.code(fastify.statusCode.OK).send(parsedReadme);
} catch {
reply.code(fastify.statusCode.OK).send(NOT_README_FOUND);
}
});
fastify.get('/package/readme/:scope/:packageName', async (request, reply) => {
// @ts-ignore
const { version, packageName } = request.params;
debug('readme name %s version: %s', packageName, version);
const manifest = (await fastify.storage?.getPackageByOptions({
name: packageName,
// remove on refactor getPackageByOptions
// @ts-ignore
req: request.raw,
version,
uplinksLook: true,
requestOptions: {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
},
})) as Manifest;
try {
reply.code(fastify.statusCode.OK).send(manifest.readme);
} catch {
reply.code(fastify.statusCode.OK).send(NOT_README_FOUND);
}
});
}
export default readmeRoute;

View File

@@ -1,82 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { Manifest, Version } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:web:sidebar');
export type $SidebarPackage = Manifest & { latest: Version };
const stringType = { type: 'string' };
const packageNameSchema = { packageName: stringType };
const paramsSchema = {
scope: stringType,
packageName: stringType,
};
async function sidebarRoute(fastify: FastifyInstance) {
fastify.get(
'/sidebar/:scope/:packageName',
{
schema: {
params: paramsSchema,
},
},
async (request, reply) => {
// @ts-ignore
const { packageName, scope } = request.params;
debug('pkg name %s, scope %s ', packageName, scope);
reply.code(fastify.statusCode.NOT_FOUND);
}
);
fastify.get(
'/sidebar/:packageName',
{
schema: {
params: packageNameSchema,
},
},
async (request, reply) => {
// @ts-ignore
const { packageName, scope } = request.params;
debug('pkg name %s, scope %s ', packageName, scope);
reply.code(fastify.statusCode.NOT_FOUND);
}
);
}
// function getSidebar(fastify: FastifyInstance, request: any, packageName, callback) {
// // fastify.storage.getPackage({
// // name: packageName,
// // uplinksLook: true,
// // keepUpLinkData: true,
// // req: request.raw,
// // callback: function (err: Error, info: $SidebarPackage): void {
// // debug('sidebar pkg info %o', info);
// // if (_.isNil(err)) {
// // const { v } = request.query;
// // let sideBarInfo = _.clone(info);
// // sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(
// // info,
// // { protocol: request.protocol, headers: request.headers as any, host: request.hostname },
// // fastify.configInstance.url_prefix
// // ).versions;
// // if (typeof v === 'string' && isVersionValid(info, v)) {
// // sideBarInfo.latest = sideBarInfo.versions[v];
// // sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
// // } else {
// // sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];
// // sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
// // }
// // sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);
// // const authorAvatar = fastify.configInstance.web
// // ? addGravatarSupport(sideBarInfo, fastify.configInstance.web.gravatar)
// // : addGravatarSupport(sideBarInfo);
// // callback(null, authorAvatar);
// // } else {
// // callback(fastify.statusCode.NOT_FOUND).send(err);
// // }
// // },
// // });
// reply.code(fastify.statusCode.NOT_FOUND);
// }
export default sidebarRoute;

View File

@@ -1,74 +0,0 @@
import buildDebug from 'debug';
import fastify from 'fastify';
import { Config as AppConfig, createAnonymousRemoteUser } from '@verdaccio/config';
import { logger } from '@verdaccio/logger';
import { ConfigYaml, Config as IConfig, RemoteUser } from '@verdaccio/types';
import distTags from './endpoints/dist-tags';
import manifest from './endpoints/manifest';
import ping from './endpoints/ping';
import search from './endpoints/search';
import tarball from './endpoints/tarball';
import user from './endpoints/user';
import whoami from './endpoints/whoami';
import authPlugin from './plugins/auth';
import configPlugin from './plugins/config';
import coreUtils from './plugins/coreUtils';
import storagePlugin from './plugins/storage';
import login from './routes/web/api/login';
import readme from './routes/web/api/readme';
import sidebar from './routes/web/api/sidebar';
const debug = buildDebug('verdaccio:fastify');
enum PREFIX {
WEB = '/-/verdaccio',
USER = '/-/user',
}
async function startServer(config: ConfigYaml): Promise<any> {
// eslint-disable-next-line prettier/prettier
const configInstance: IConfig = new AppConfig({ ...config } as any);
debug('start fastify server');
// TODO: custom logger type and logger accepted by fastify does not match
const fastifyInstance = fastify({ logger: logger as any });
fastifyInstance.addHook('onRequest', (request, reply, done) => {
request.userRemote = createAnonymousRemoteUser();
done();
});
fastifyInstance.register(coreUtils);
fastifyInstance.register(configPlugin, { config });
fastifyInstance.register(storagePlugin, { config: configInstance });
fastifyInstance.register(authPlugin, { config: configInstance });
// api
fastifyInstance.register((instance, opts, done) => {
instance.register(ping);
instance.register(user, { prefix: PREFIX.USER });
instance.register(search);
instance.register(whoami);
instance.register(manifest);
instance.register(tarball);
instance.register(distTags);
instance.register(readme, { prefix: PREFIX.WEB });
instance.register(sidebar, { prefix: PREFIX.WEB });
instance.register(login, { prefix: PREFIX.WEB });
done();
});
// web
fastifyInstance.register((instance, opts, done) => {
instance.register(ping, { prefix: '/web' });
done();
});
return fastifyInstance;
}
declare module 'fastify' {
interface FastifyRequest {
userRemote: RemoteUser;
}
}
export default startServer;

View File

@@ -1,9 +0,0 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"]
}

View File

@@ -1,29 +0,0 @@
{
"extends": "../../../tsconfig.reference.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"],
"references": [
{
"path": "../../store"
},
{
"path": "../../config"
},
{
"path": "../../auth"
},
{
"path": "../../logger/logger"
},
{
"path": "../../utils"
},
{
"path": "../../core/core"
}
]
}

View File

@@ -57,7 +57,6 @@
"@verdaccio/store": "workspace:8.0.0-next-8.24",
"@verdaccio/test-helper": "workspace:4.0.0-next-8.8",
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"fastify": "4.25.2",
"get-port": "5.1.1",
"got": "11.8.6",
"lodash": "4.17.21",

View File

@@ -1,29 +0,0 @@
import fastify, { FastifyInstance } from 'fastify';
/**
* Simple Server
*
* A empty express server with the objective to emumate any external API.
*
* eg: test/functional/tags/tags.ts
*
* express.get('/testexp_tags', function(req, res) {
let f = readTags().toString().replace(/__NAME__/g, 'testexp_tags');
res.send(JSON.parse(f));
});
*
* or at test/functional/package/gzip.ts
*/
export default class SimpleServer {
public server: FastifyInstance;
public constructor() {
this.server = fastify({ logger: true });
}
public async start(port: number = 55550): Promise<void> {
await this.server.listen(port);
// prevent keeping the process running.
this.server.server.unref();
}
}

556
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff