@verdaccio/web 8.1.0-next-8.12 → 8.1.0-next-8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/package.json +22 -18
- package/.babelrc +0 -3
- package/CHANGELOG.md +0 -2516
- package/src/api/index.ts +0 -30
- package/src/api/package.ts +0 -111
- package/src/api/readme.ts +0 -101
- package/src/api/search.ts +0 -92
- package/src/api/sidebar.ts +0 -91
- package/src/api/user.ts +0 -94
- package/src/index.ts +0 -2
- package/src/middleware.ts +0 -64
- package/src/web-utils.ts +0 -28
- package/test/api.packages.test.ts +0 -90
- package/test/api.readme.test.ts +0 -105
- package/test/api.search.test.ts +0 -68
- package/test/api.sidebar.test.ts +0 -51
- package/test/api.user.test.ts +0 -256
- package/test/config/change-password-disabled.yaml +0 -28
- package/test/config/default-test.yaml +0 -28
- package/test/config/keep-all-readmes.yaml +0 -29
- package/test/config/login-disabled.yaml +0 -29
- package/test/config/web.yaml +0 -46
- package/test/helper.ts +0 -26
- package/test/partials/htmlParser.ts +0 -1
- package/test/partials/manifest/manifest.json +0 -64
- package/test/partials/readme/ascii.adoc +0 -12
- package/test/partials/search/search-v1.json +0 -157
- package/test/static/main.js +0 -3
- package/test/static/runtime.js +0 -3
- package/test/static/vendors.js +0 -3
- package/test/web-utils.spec.ts +0 -46
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -50
- package/types/custom.d.ts +0 -10
package/src/api/index.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
|
|
3
|
-
import { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';
|
|
4
|
-
|
|
5
|
-
import { hasLogin } from '../web-utils';
|
|
6
|
-
import packageApi from './package';
|
|
7
|
-
import readme from './readme';
|
|
8
|
-
import search from './search';
|
|
9
|
-
import sidebar from './sidebar';
|
|
10
|
-
import user from './user';
|
|
11
|
-
|
|
12
|
-
export default (auth, storage, config) => {
|
|
13
|
-
const route = Router(); /* eslint new-cap: 0 */
|
|
14
|
-
route.use(
|
|
15
|
-
WebUrlsNamespace.data,
|
|
16
|
-
rateLimit({
|
|
17
|
-
windowMs: 2 * 60 * 1000, // 2 minutes
|
|
18
|
-
max: 5000, // limit each IP to 1000 requests per windowMs
|
|
19
|
-
...config?.web?.rateLimit,
|
|
20
|
-
})
|
|
21
|
-
);
|
|
22
|
-
route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));
|
|
23
|
-
route.use(WebUrlsNamespace.data, search(storage, auth));
|
|
24
|
-
route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));
|
|
25
|
-
route.use(WebUrlsNamespace.data, readme(storage, auth));
|
|
26
|
-
if (hasLogin(config)) {
|
|
27
|
-
route.use(WebUrlsNamespace.sec, user(auth, config));
|
|
28
|
-
}
|
|
29
|
-
return route;
|
|
30
|
-
};
|
package/src/api/package.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import buildDebug from 'debug';
|
|
2
|
-
import { Router } from 'express';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
|
|
5
|
-
import { Auth } from '@verdaccio/auth';
|
|
6
|
-
import { createAnonymousRemoteUser } from '@verdaccio/config';
|
|
7
|
-
import { logger } from '@verdaccio/logger';
|
|
8
|
-
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';
|
|
9
|
-
import { WebUrls } from '@verdaccio/middleware';
|
|
10
|
-
import { Storage } from '@verdaccio/store';
|
|
11
|
-
import { getLocalRegistryTarballUri } from '@verdaccio/tarball';
|
|
12
|
-
import { Config, RemoteUser, Version } from '@verdaccio/types';
|
|
13
|
-
import { formatAuthor, generateGravatarUrl } from '@verdaccio/utils';
|
|
14
|
-
|
|
15
|
-
import { sortByName } from '../web-utils';
|
|
16
|
-
|
|
17
|
-
export { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages
|
|
18
|
-
|
|
19
|
-
const getOrder = (order = 'asc') => {
|
|
20
|
-
return order === 'asc';
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const debug = buildDebug('verdaccio:web:api:package');
|
|
24
|
-
|
|
25
|
-
function addPackageWebApi(storage: Storage, auth: Auth, config: Config): Router {
|
|
26
|
-
const isLoginEnabled = config?.web?.login === true;
|
|
27
|
-
const pkgRouter = Router(); /* eslint new-cap: 0 */
|
|
28
|
-
const anonymousRemoteUser: RemoteUser = createAnonymousRemoteUser();
|
|
29
|
-
|
|
30
|
-
debug('initialized package web api');
|
|
31
|
-
const checkAllow = (name: string, remoteUser: RemoteUser): Promise<boolean> =>
|
|
32
|
-
new Promise((resolve, reject): void => {
|
|
33
|
-
debug('is login enabled: %o', isLoginEnabled);
|
|
34
|
-
const remoteUserAccess = !isLoginEnabled ? anonymousRemoteUser : remoteUser;
|
|
35
|
-
try {
|
|
36
|
-
auth.allow_access({ packageName: name }, remoteUserAccess, (err, allowed): void => {
|
|
37
|
-
if (err) {
|
|
38
|
-
resolve(false);
|
|
39
|
-
}
|
|
40
|
-
return resolve(allowed as boolean);
|
|
41
|
-
});
|
|
42
|
-
} catch (err: any) {
|
|
43
|
-
reject(err);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
async function processPackages(packages: Version[] = [], req): Promise<Version[]> {
|
|
48
|
-
const permissions: Version[] = [];
|
|
49
|
-
const packagesToProcess = packages.slice();
|
|
50
|
-
debug('process packages %o', packagesToProcess);
|
|
51
|
-
for (const pkg of packagesToProcess) {
|
|
52
|
-
const pkgCopy = { ...pkg };
|
|
53
|
-
pkgCopy.author = formatAuthor(pkg.author);
|
|
54
|
-
try {
|
|
55
|
-
if (await checkAllow(pkg.name, req.remote_user)) {
|
|
56
|
-
if (config.web) {
|
|
57
|
-
// @ts-ignore
|
|
58
|
-
pkgCopy.author.avatar = generateGravatarUrl(pkgCopy.author.email, config.web.gravatar);
|
|
59
|
-
}
|
|
60
|
-
// convert any remote dist to a local reference
|
|
61
|
-
// eg: if the dist points to npmjs, switch to localhost:4873/prefix/etc.tar.gz
|
|
62
|
-
if (!_.isNil(pkgCopy.dist) && !_.isNull(pkgCopy.dist.tarball)) {
|
|
63
|
-
pkgCopy.dist.tarball = getLocalRegistryTarballUri(
|
|
64
|
-
pkgCopy.dist.tarball,
|
|
65
|
-
pkg.name,
|
|
66
|
-
{ protocol: req.protocol, headers: req.headers as any, host: req.hostname },
|
|
67
|
-
config?.url_prefix
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
permissions.push(pkgCopy);
|
|
71
|
-
}
|
|
72
|
-
} catch (err: any) {
|
|
73
|
-
debug('process packages error %o', err);
|
|
74
|
-
logger.error(
|
|
75
|
-
{ name: pkg.name, error: err },
|
|
76
|
-
'permission process for @{name} has failed: @{error}'
|
|
77
|
-
);
|
|
78
|
-
throw err;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return permissions;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Get list of all visible package
|
|
86
|
-
pkgRouter.get(
|
|
87
|
-
WebUrls.packages_all,
|
|
88
|
-
async function (
|
|
89
|
-
req: $RequestExtend,
|
|
90
|
-
res: $ResponseExtend,
|
|
91
|
-
next: $NextFunctionVer
|
|
92
|
-
): Promise<void> {
|
|
93
|
-
debug('hit package web api %o');
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const localPackages: Version[] = await storage.getLocalDatabase();
|
|
97
|
-
|
|
98
|
-
const order = getOrder(config?.web?.sort_packages);
|
|
99
|
-
debug('order %o', order);
|
|
100
|
-
const pkgs = await processPackages(localPackages, req);
|
|
101
|
-
next(sortByName(pkgs, order));
|
|
102
|
-
} catch (error: any) {
|
|
103
|
-
next(error);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
return pkgRouter;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export default addPackageWebApi;
|
package/src/api/readme.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import buildDebug from 'debug';
|
|
2
|
-
import { Router } from 'express';
|
|
3
|
-
|
|
4
|
-
import { Auth } from '@verdaccio/auth';
|
|
5
|
-
import { DIST_TAGS, HEADERS, HEADER_TYPE } from '@verdaccio/core';
|
|
6
|
-
import { logger } from '@verdaccio/logger';
|
|
7
|
-
import { $NextFunctionVer, $RequestExtend, $ResponseExtend, allow } from '@verdaccio/middleware';
|
|
8
|
-
// Was required by other packages
|
|
9
|
-
import { WebUrls } from '@verdaccio/middleware';
|
|
10
|
-
import { Storage } from '@verdaccio/store';
|
|
11
|
-
import { Manifest } from '@verdaccio/types';
|
|
12
|
-
import { isVersionValid } from '@verdaccio/utils';
|
|
13
|
-
|
|
14
|
-
import { AuthorAvatar, addScope } from '../web-utils';
|
|
15
|
-
|
|
16
|
-
export { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages
|
|
17
|
-
|
|
18
|
-
// TODO: review this type, should be on @verdacid/types
|
|
19
|
-
export type PackageExt = Manifest & { author: AuthorAvatar; dist?: { tarball: string } };
|
|
20
|
-
export const NOT_README_FOUND = 'ERROR: No README data found!';
|
|
21
|
-
const debug = buildDebug('verdaccio:web:api:readme');
|
|
22
|
-
|
|
23
|
-
const getReadme = (readme) => {
|
|
24
|
-
if (typeof readme === 'string' && readme.length === 0) {
|
|
25
|
-
return NOT_README_FOUND;
|
|
26
|
-
}
|
|
27
|
-
if (typeof readme !== 'string') {
|
|
28
|
-
return NOT_README_FOUND;
|
|
29
|
-
} else {
|
|
30
|
-
return readme;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {
|
|
35
|
-
let id;
|
|
36
|
-
let readme;
|
|
37
|
-
if (typeof v === 'string' && isVersionValid(manifest, v)) {
|
|
38
|
-
id = 'version';
|
|
39
|
-
readme = manifest.versions[v].readme;
|
|
40
|
-
}
|
|
41
|
-
if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {
|
|
42
|
-
id = 'latest';
|
|
43
|
-
readme = manifest.versions[manifest[DIST_TAGS].latest].readme;
|
|
44
|
-
}
|
|
45
|
-
if (!readme && manifest.readme) {
|
|
46
|
-
id = 'root';
|
|
47
|
-
readme = manifest.readme;
|
|
48
|
-
}
|
|
49
|
-
debug('readme: %o %o', v, id);
|
|
50
|
-
return readme;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
function addReadmeWebApi(storage: Storage, auth: Auth): Router {
|
|
54
|
-
debug('initialized readme web api');
|
|
55
|
-
const can = allow(auth, {
|
|
56
|
-
beforeAll: (a, b) => logger.trace(a, b),
|
|
57
|
-
afterAll: (a, b) => logger.trace(a, b),
|
|
58
|
-
});
|
|
59
|
-
const pkgRouter = Router(); /* eslint new-cap: 0 */
|
|
60
|
-
|
|
61
|
-
pkgRouter.get(
|
|
62
|
-
[WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],
|
|
63
|
-
can('access'),
|
|
64
|
-
async function (
|
|
65
|
-
req: $RequestExtend,
|
|
66
|
-
res: $ResponseExtend,
|
|
67
|
-
next: $NextFunctionVer
|
|
68
|
-
): Promise<void> {
|
|
69
|
-
debug('readme hit');
|
|
70
|
-
const rawScope = req.params.scope; // May include '@'
|
|
71
|
-
const scope = rawScope ? rawScope.slice(1) : null; // Remove '@' if present
|
|
72
|
-
const name = scope ? addScope(scope, req.params.package) : req.params.package;
|
|
73
|
-
debug('readme name %o', name);
|
|
74
|
-
const requestOptions = {
|
|
75
|
-
protocol: req.protocol,
|
|
76
|
-
headers: req.headers as any,
|
|
77
|
-
// FIXME: if we migrate to req.hostname, the port is not longer included.
|
|
78
|
-
host: req.host,
|
|
79
|
-
remoteAddress: req.socket.remoteAddress,
|
|
80
|
-
};
|
|
81
|
-
try {
|
|
82
|
-
const manifest = (await storage.getPackageByOptions({
|
|
83
|
-
name,
|
|
84
|
-
uplinksLook: true,
|
|
85
|
-
abbreviated: false,
|
|
86
|
-
requestOptions,
|
|
87
|
-
})) as Manifest;
|
|
88
|
-
debug('readme pkg %o', manifest?.name);
|
|
89
|
-
res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);
|
|
90
|
-
const { v } = req.query;
|
|
91
|
-
const readme = getReadmeFromManifest(manifest, v);
|
|
92
|
-
next(getReadme(readme));
|
|
93
|
-
} catch (err) {
|
|
94
|
-
next(err);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
return pkgRouter;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export default addReadmeWebApi;
|
package/src/api/search.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import buildDebug from 'debug';
|
|
2
|
-
import { Router } from 'express';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
import { URLSearchParams } from 'url';
|
|
5
|
-
|
|
6
|
-
import { Auth } from '@verdaccio/auth';
|
|
7
|
-
import { errorUtils, searchUtils } from '@verdaccio/core';
|
|
8
|
-
import { SearchQuery } from '@verdaccio/core/src/search-utils';
|
|
9
|
-
import { WebUrls } from '@verdaccio/middleware';
|
|
10
|
-
import { Storage } from '@verdaccio/store';
|
|
11
|
-
import { Manifest } from '@verdaccio/types';
|
|
12
|
-
|
|
13
|
-
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from './package';
|
|
14
|
-
|
|
15
|
-
const debug = buildDebug('verdaccio:web:api:search');
|
|
16
|
-
|
|
17
|
-
function checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {
|
|
20
|
-
if (err) {
|
|
21
|
-
if (err.status && String(err.status).match(/^4\d\d$/)) {
|
|
22
|
-
// auth plugin returns 4xx user error,
|
|
23
|
-
// that's equivalent of !allowed basically
|
|
24
|
-
allowed = false;
|
|
25
|
-
return resolve(null);
|
|
26
|
-
} else {
|
|
27
|
-
reject(err);
|
|
28
|
-
}
|
|
29
|
-
} else {
|
|
30
|
-
return resolve(allowed ? pkg : null);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function addSearchWebApi(storage: Storage, auth: Auth): Router {
|
|
37
|
-
const router = Router(); /* eslint new-cap: 0 */
|
|
38
|
-
router.get(
|
|
39
|
-
WebUrls.search,
|
|
40
|
-
async function (
|
|
41
|
-
req: $RequestExtend,
|
|
42
|
-
res: $ResponseExtend,
|
|
43
|
-
next: $NextFunctionVer
|
|
44
|
-
): Promise<void> {
|
|
45
|
-
try {
|
|
46
|
-
let data;
|
|
47
|
-
const abort = new AbortController();
|
|
48
|
-
req.socket.on('close', function () {
|
|
49
|
-
debug('search web aborted');
|
|
50
|
-
abort.abort();
|
|
51
|
-
});
|
|
52
|
-
const text: string = (req.params.anything as string) ?? '';
|
|
53
|
-
// These values are declared as optimal by npm cli
|
|
54
|
-
// FUTURE: could be overwritten by ui settings.
|
|
55
|
-
const size = 20;
|
|
56
|
-
const from = 0;
|
|
57
|
-
const query: SearchQuery = {
|
|
58
|
-
from: 0,
|
|
59
|
-
maintenance: 0.5,
|
|
60
|
-
popularity: 0.98,
|
|
61
|
-
quality: 0.65,
|
|
62
|
-
size: 20,
|
|
63
|
-
text,
|
|
64
|
-
};
|
|
65
|
-
// @ts-ignore
|
|
66
|
-
const urlParams = new URLSearchParams(query);
|
|
67
|
-
debug('search web init');
|
|
68
|
-
data = await storage?.search({
|
|
69
|
-
query,
|
|
70
|
-
url: `/-/v1/search?${urlParams.toString()}`,
|
|
71
|
-
abort,
|
|
72
|
-
});
|
|
73
|
-
const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(
|
|
74
|
-
data.map((pkgItem) => {
|
|
75
|
-
return checkAccess(pkgItem, auth, req.remote_user);
|
|
76
|
-
})
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const final: searchUtils.SearchItemPkg[] = checkAccessPromises
|
|
80
|
-
.filter((i) => !_.isNull(i))
|
|
81
|
-
.slice(from, size);
|
|
82
|
-
|
|
83
|
-
next(final);
|
|
84
|
-
} catch (err: any) {
|
|
85
|
-
next(errorUtils.getInternalError(err.message));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
);
|
|
89
|
-
return router;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export default addSearchWebApi;
|
package/src/api/sidebar.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import buildDebug from 'debug';
|
|
2
|
-
import { Router } from 'express';
|
|
3
|
-
|
|
4
|
-
import { Auth } from '@verdaccio/auth';
|
|
5
|
-
import { DIST_TAGS, HTTP_STATUS } from '@verdaccio/core';
|
|
6
|
-
import { logger } from '@verdaccio/logger';
|
|
7
|
-
import { $NextFunctionVer, $RequestExtend, $ResponseExtend, allow } from '@verdaccio/middleware';
|
|
8
|
-
// Was required by other packages
|
|
9
|
-
import { WebUrls } from '@verdaccio/middleware';
|
|
10
|
-
import { Storage } from '@verdaccio/store';
|
|
11
|
-
import { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';
|
|
12
|
-
import { Config, Manifest, Version } from '@verdaccio/types';
|
|
13
|
-
import { addGravatarSupport, formatAuthor, isVersionValid } from '@verdaccio/utils';
|
|
14
|
-
|
|
15
|
-
import { AuthorAvatar, addScope, deleteProperties } from '../web-utils';
|
|
16
|
-
|
|
17
|
-
export { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages
|
|
18
|
-
|
|
19
|
-
export type PackageExt = Manifest & { author: AuthorAvatar; dist?: { tarball: string } };
|
|
20
|
-
|
|
21
|
-
export type $SidebarPackage = Manifest & { latest: Version };
|
|
22
|
-
const debug = buildDebug('verdaccio:web:api:sidebar');
|
|
23
|
-
|
|
24
|
-
function addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {
|
|
25
|
-
debug('initialized sidebar web api');
|
|
26
|
-
const router = Router(); /* eslint new-cap: 0 */
|
|
27
|
-
const can = allow(auth, {
|
|
28
|
-
beforeAll: (a, b) => logger.trace(a, b),
|
|
29
|
-
afterAll: (a, b) => logger.trace(a, b),
|
|
30
|
-
});
|
|
31
|
-
// Get package sidebar
|
|
32
|
-
router.get(
|
|
33
|
-
[WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],
|
|
34
|
-
can('access'),
|
|
35
|
-
async function (
|
|
36
|
-
req: $RequestExtend,
|
|
37
|
-
res: $ResponseExtend,
|
|
38
|
-
next: $NextFunctionVer
|
|
39
|
-
): Promise<void> {
|
|
40
|
-
const rawScope = req.params.scope; // May include '@'
|
|
41
|
-
const scope = rawScope ? rawScope.slice(1) : null; // Remove '@' if present
|
|
42
|
-
const name: string = scope ? addScope(scope, req.params.package) : req.params.package;
|
|
43
|
-
const requestOptions = {
|
|
44
|
-
protocol: req.protocol,
|
|
45
|
-
headers: req.headers as any,
|
|
46
|
-
// FIXME: if we migrate to req.hostname, the port is not longer included.
|
|
47
|
-
host: req.host,
|
|
48
|
-
remoteAddress: req.socket.remoteAddress,
|
|
49
|
-
};
|
|
50
|
-
try {
|
|
51
|
-
const info = (await storage.getPackageByOptions({
|
|
52
|
-
name,
|
|
53
|
-
uplinksLook: true,
|
|
54
|
-
keepUpLinkData: true,
|
|
55
|
-
requestOptions,
|
|
56
|
-
})) as Manifest;
|
|
57
|
-
const { v } = req.query;
|
|
58
|
-
let sideBarInfo = { ...info };
|
|
59
|
-
sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(
|
|
60
|
-
info,
|
|
61
|
-
{ protocol: req.protocol, headers: req.headers as any, host: req.hostname },
|
|
62
|
-
config.url_prefix
|
|
63
|
-
).versions;
|
|
64
|
-
// TODO: review this implementation
|
|
65
|
-
if (typeof v === 'string' && isVersionValid(info, v)) {
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
sideBarInfo.latest = sideBarInfo.versions[v];
|
|
68
|
-
// @ts-ignore
|
|
69
|
-
sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
|
|
70
|
-
} else {
|
|
71
|
-
// @ts-ignore
|
|
72
|
-
sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];
|
|
73
|
-
// @ts-ignore
|
|
74
|
-
sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
|
|
75
|
-
}
|
|
76
|
-
sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);
|
|
77
|
-
const authorAvatar = config.web
|
|
78
|
-
? addGravatarSupport(sideBarInfo, config.web.gravatar)
|
|
79
|
-
: addGravatarSupport(sideBarInfo);
|
|
80
|
-
next(authorAvatar);
|
|
81
|
-
} catch (err) {
|
|
82
|
-
res.status(HTTP_STATUS.NOT_FOUND);
|
|
83
|
-
res.end();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return router;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export default addSidebarWebApi;
|
package/src/api/user.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import buildDebug from 'debug';
|
|
2
|
-
import { Request, Response, Router } from 'express';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
|
|
5
|
-
import { Auth } from '@verdaccio/auth';
|
|
6
|
-
import {
|
|
7
|
-
API_ERROR,
|
|
8
|
-
APP_ERROR,
|
|
9
|
-
HEADERS,
|
|
10
|
-
HTTP_STATUS,
|
|
11
|
-
VerdaccioError,
|
|
12
|
-
errorUtils,
|
|
13
|
-
validatioUtils,
|
|
14
|
-
} from '@verdaccio/core';
|
|
15
|
-
import { rateLimit } from '@verdaccio/middleware';
|
|
16
|
-
import { WebUrls } from '@verdaccio/middleware';
|
|
17
|
-
import { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';
|
|
18
|
-
|
|
19
|
-
import { $NextFunctionVer } from './package';
|
|
20
|
-
|
|
21
|
-
const debug = buildDebug('verdaccio:web:api:user');
|
|
22
|
-
|
|
23
|
-
function addUserAuthApi(auth: Auth, config: Config): Router {
|
|
24
|
-
const route = Router(); /* eslint new-cap: 0 */
|
|
25
|
-
route.post(
|
|
26
|
-
WebUrls.user_login,
|
|
27
|
-
rateLimit(config?.userRateLimit),
|
|
28
|
-
function (req: Request, res: Response, next: $NextFunctionVer): void {
|
|
29
|
-
const { username, password } = req.body;
|
|
30
|
-
debug('authenticate %o', username);
|
|
31
|
-
auth.authenticate(
|
|
32
|
-
username,
|
|
33
|
-
password,
|
|
34
|
-
async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {
|
|
35
|
-
if (err) {
|
|
36
|
-
const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;
|
|
37
|
-
debug('error authenticate %o', errorCode);
|
|
38
|
-
next(errorUtils.getCode(errorCode, err.message));
|
|
39
|
-
} else {
|
|
40
|
-
req.remote_user = user as RemoteUser;
|
|
41
|
-
const jWTSignOptions: JWTSignOptions = config.security.web.sign;
|
|
42
|
-
res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
|
|
43
|
-
next({
|
|
44
|
-
token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),
|
|
45
|
-
username: req.remote_user.name,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
if (config?.flags?.changePassword === true) {
|
|
54
|
-
route.put(
|
|
55
|
-
WebUrls.reset_password,
|
|
56
|
-
rateLimit(config?.userRateLimit),
|
|
57
|
-
function (req: Request, res: Response, next: $NextFunctionVer): void {
|
|
58
|
-
if (_.isNil(req.remote_user.name)) {
|
|
59
|
-
res.status(HTTP_STATUS.UNAUTHORIZED);
|
|
60
|
-
return next({
|
|
61
|
-
// FUTURE: update to a more meaningful message
|
|
62
|
-
message: API_ERROR.MUST_BE_LOGGED,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const { password } = req.body;
|
|
67
|
-
const { name } = req.remote_user;
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
validatioUtils.validatePassword(
|
|
71
|
-
password.new,
|
|
72
|
-
config?.serverSettings?.passwordValidationRegex
|
|
73
|
-
) === false
|
|
74
|
-
) {
|
|
75
|
-
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {
|
|
79
|
-
if (_.isNil(err) && isUpdated) {
|
|
80
|
-
next({
|
|
81
|
-
ok: true,
|
|
82
|
-
});
|
|
83
|
-
} else {
|
|
84
|
-
return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return route;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export default addUserAuthApi;
|
package/src/index.ts
DELETED
package/src/middleware.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import _ from 'lodash';
|
|
3
|
-
|
|
4
|
-
import { PLUGIN_CATEGORY } from '@verdaccio/core';
|
|
5
|
-
import { asyncLoadPlugin } from '@verdaccio/loaders';
|
|
6
|
-
import { logger } from '@verdaccio/logger';
|
|
7
|
-
import { webMiddleware } from '@verdaccio/middleware';
|
|
8
|
-
|
|
9
|
-
import webEndpointsApi from './api';
|
|
10
|
-
|
|
11
|
-
export const PLUGIN_UI_PREFIX = 'verdaccio-theme';
|
|
12
|
-
export const DEFAULT_PLUGIN_UI_THEME = '@verdaccio/ui-theme';
|
|
13
|
-
|
|
14
|
-
export async function loadTheme(config: any) {
|
|
15
|
-
if (_.isNil(config.theme) === false) {
|
|
16
|
-
const plugin = await asyncLoadPlugin(
|
|
17
|
-
config.theme,
|
|
18
|
-
{ config, logger },
|
|
19
|
-
// TODO: add types { staticPath: string; manifest: unknown; manifestFiles: unknown }
|
|
20
|
-
function (plugin: any) {
|
|
21
|
-
/**
|
|
22
|
-
*
|
|
23
|
-
- `staticPath`: is the same data returned in Verdaccio 5.
|
|
24
|
-
- `manifest`: A webpack manifest object.
|
|
25
|
-
- `manifestFiles`: A object with one property `js` and the array (order matters) of the manifest id to be loaded in the template dynamically.
|
|
26
|
-
*/
|
|
27
|
-
return plugin.staticPath && plugin.manifest && plugin.manifestFiles;
|
|
28
|
-
},
|
|
29
|
-
config?.serverSettings?.pluginPrefix ?? PLUGIN_UI_PREFIX,
|
|
30
|
-
PLUGIN_CATEGORY.THEME
|
|
31
|
-
);
|
|
32
|
-
if (plugin.length > 1) {
|
|
33
|
-
logger.warn('multiple ui themes are not supported; only the first plugin is used');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return _.head(plugin);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default async (config, auth, storage, logger) => {
|
|
41
|
-
let pluginOptions = await loadTheme(config);
|
|
42
|
-
if (!pluginOptions) {
|
|
43
|
-
pluginOptions = require(DEFAULT_PLUGIN_UI_THEME)(config.web);
|
|
44
|
-
logger.info(
|
|
45
|
-
{ name: DEFAULT_PLUGIN_UI_THEME, pluginCategory: PLUGIN_CATEGORY.THEME },
|
|
46
|
-
'plugin @{name} successfully loaded (@{pluginCategory})'
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// eslint-disable-next-line new-cap
|
|
51
|
-
const router = express.Router();
|
|
52
|
-
// load application
|
|
53
|
-
router.use(
|
|
54
|
-
webMiddleware(
|
|
55
|
-
config,
|
|
56
|
-
{
|
|
57
|
-
tokenMiddleware: auth.webUIJWTmiddleware(),
|
|
58
|
-
webEndpointsApi: webEndpointsApi(auth, storage, config),
|
|
59
|
-
},
|
|
60
|
-
pluginOptions
|
|
61
|
-
)
|
|
62
|
-
);
|
|
63
|
-
return router;
|
|
64
|
-
};
|
package/src/web-utils.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
|
|
3
|
-
import { Author, ConfigYaml } from '@verdaccio/types';
|
|
4
|
-
|
|
5
|
-
export function hasLogin(config: ConfigYaml) {
|
|
6
|
-
return _.isNil(config?.web?.login) || config?.web?.login === true;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {
|
|
10
|
-
return packages.slice().sort(function (a, b): number {
|
|
11
|
-
const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();
|
|
12
|
-
return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type AuthorAvatar = Author & { avatar?: string };
|
|
17
|
-
|
|
18
|
-
export function addScope(scope: string, packageName: string): string {
|
|
19
|
-
return `@${scope}/${packageName}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function deleteProperties(propertiesToDelete: string[], objectItem: any): any {
|
|
23
|
-
_.forEach(propertiesToDelete, (property): any => {
|
|
24
|
-
delete objectItem[property];
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
return objectItem;
|
|
28
|
-
}
|