2020-08-19 20:27:35 +02:00
|
|
|
import buildDebug from 'debug';
|
2021-10-29 17:33:05 +02:00
|
|
|
import { Router } from 'express';
|
|
|
|
import _ from 'lodash';
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2020-09-17 06:48:16 +02:00
|
|
|
import { IAuth } from '@verdaccio/auth';
|
2021-10-29 17:33:05 +02:00
|
|
|
import { API_ERROR, DIST_TAGS, HEADERS, errorUtils } from '@verdaccio/core';
|
|
|
|
import { allow } from '@verdaccio/middleware';
|
2021-09-08 19:06:37 +02:00
|
|
|
import { Storage } from '@verdaccio/store';
|
2021-03-06 18:56:45 +01:00
|
|
|
import { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';
|
2021-10-29 17:33:05 +02:00
|
|
|
import { Config, Package } from '@verdaccio/types';
|
|
|
|
import { getVersion } from '@verdaccio/utils';
|
|
|
|
|
|
|
|
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2020-08-19 20:27:35 +02:00
|
|
|
const debug = buildDebug('verdaccio:api:package');
|
|
|
|
|
2020-09-17 06:48:16 +02:00
|
|
|
const downloadStream = (
|
|
|
|
packageName: string,
|
|
|
|
filename: string,
|
|
|
|
storage: any,
|
|
|
|
req: $RequestExtend,
|
|
|
|
res: $ResponseExtend
|
|
|
|
): void => {
|
2020-03-03 23:59:19 +01:00
|
|
|
const stream = storage.getTarball(packageName, filename);
|
|
|
|
|
2020-08-13 23:27:00 +02:00
|
|
|
stream.on('content-length', function (content): void {
|
2020-03-03 23:59:19 +01:00
|
|
|
res.header('Content-Length', content);
|
|
|
|
});
|
|
|
|
|
2020-08-13 23:27:00 +02:00
|
|
|
stream.on('error', function (err): void {
|
2020-06-30 21:55:14 +02:00
|
|
|
return res.locals.report_error(err);
|
2020-03-03 23:59:19 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
res.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);
|
|
|
|
stream.pipe(res);
|
|
|
|
};
|
|
|
|
|
2021-09-08 19:06:37 +02:00
|
|
|
export default function (route: Router, auth: IAuth, storage: Storage, config: Config): void {
|
2020-03-03 23:59:19 +01:00
|
|
|
const can = allow(auth);
|
|
|
|
// TODO: anonymous user?
|
2021-01-16 10:40:43 +01:00
|
|
|
route.get(
|
|
|
|
'/:package/:version?',
|
|
|
|
can('access'),
|
2021-10-29 09:00:02 +02:00
|
|
|
function (req: $RequestExtend, _res: $ResponseExtend, next: $NextFunctionVer): void {
|
2021-01-16 10:40:43 +01:00
|
|
|
debug('init package by version');
|
|
|
|
const name = req.params.package;
|
2021-11-09 20:38:44 +01:00
|
|
|
let queryVersion = req.params.version;
|
|
|
|
const requestOptions = {
|
|
|
|
protocol: req.protocol,
|
|
|
|
headers: req.headers as any,
|
|
|
|
// FIXME: if we migrate to req.hostname, the port is not longer included.
|
|
|
|
host: req.host,
|
|
|
|
};
|
2021-01-16 10:40:43 +01:00
|
|
|
const getPackageMetaCallback = function (err, metadata: Package): void {
|
|
|
|
if (err) {
|
|
|
|
debug('error on fetch metadata for %o with error %o', name, err.message);
|
|
|
|
return next(err);
|
|
|
|
}
|
|
|
|
debug('convert dist remote to local with prefix %o', config?.url_prefix);
|
2021-10-29 09:00:02 +02:00
|
|
|
metadata = convertDistRemoteToLocalTarballUrls(
|
|
|
|
metadata,
|
2021-11-09 20:38:44 +01:00
|
|
|
requestOptions,
|
2021-10-29 09:00:02 +02:00
|
|
|
config?.url_prefix
|
|
|
|
);
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
debug('query by param version: %o', queryVersion);
|
|
|
|
if (_.isNil(queryVersion)) {
|
|
|
|
debug('param %o version found', queryVersion);
|
|
|
|
return next(metadata);
|
|
|
|
}
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-11-09 20:38:44 +01:00
|
|
|
let version = getVersion(metadata.versions, queryVersion);
|
2021-01-16 10:40:43 +01:00
|
|
|
debug('query by latest version %o and result %o', queryVersion, version);
|
|
|
|
if (_.isNil(version) === false) {
|
|
|
|
debug('latest version found %o', version);
|
|
|
|
return next(version);
|
|
|
|
}
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
if (_.isNil(metadata[DIST_TAGS]) === false) {
|
|
|
|
if (_.isNil(metadata[DIST_TAGS][queryVersion]) === false) {
|
|
|
|
queryVersion = metadata[DIST_TAGS][queryVersion];
|
|
|
|
debug('dist-tag version found %o', queryVersion);
|
2021-11-09 20:38:44 +01:00
|
|
|
version = getVersion(metadata.versions, queryVersion);
|
2021-01-16 10:40:43 +01:00
|
|
|
if (_.isNil(version) === false) {
|
|
|
|
debug('dist-tag found %o', version);
|
|
|
|
return next(version);
|
|
|
|
}
|
2020-03-03 23:59:19 +01:00
|
|
|
}
|
2021-01-16 10:40:43 +01:00
|
|
|
} else {
|
|
|
|
debug('dist tag not detected');
|
2020-03-03 23:59:19 +01:00
|
|
|
}
|
2020-08-19 20:27:35 +02:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
debug('package version not found %o', queryVersion);
|
2021-09-26 00:08:00 +02:00
|
|
|
return next(errorUtils.getNotFound(`${API_ERROR.VERSION_NOT_EXIST}: ${queryVersion}`));
|
2021-01-16 10:40:43 +01:00
|
|
|
};
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
debug('get package name %o', name);
|
|
|
|
debug('uplinks look up enabled');
|
|
|
|
storage.getPackage({
|
|
|
|
name,
|
|
|
|
uplinksLook: true,
|
|
|
|
req,
|
|
|
|
callback: getPackageMetaCallback,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
route.get(
|
|
|
|
'/:scopedPackage/-/:scope/:filename',
|
|
|
|
can('access'),
|
|
|
|
function (req: $RequestExtend, res: $ResponseExtend): void {
|
|
|
|
const { scopedPackage, filename } = req.params;
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
downloadStream(scopedPackage, filename, storage, req, res);
|
|
|
|
}
|
|
|
|
);
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2021-01-16 10:40:43 +01:00
|
|
|
route.get(
|
|
|
|
'/:package/-/:filename',
|
|
|
|
can('access'),
|
|
|
|
function (req: $RequestExtend, res: $ResponseExtend): void {
|
|
|
|
downloadStream(req.params.package, req.params.filename, storage, req, res);
|
|
|
|
}
|
|
|
|
);
|
2020-03-03 23:59:19 +01:00
|
|
|
}
|