@verdaccio/api 6.0.0-6-next.25 → 6.0.0-6-next.28
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/CHANGELOG.md +106 -0
- package/build/dist-tags.js +40 -35
- package/build/dist-tags.js.map +1 -1
- package/build/index.js +1 -7
- package/build/index.js.map +1 -1
- package/build/package.js +39 -28
- package/build/package.js.map +1 -1
- package/build/ping.js.map +1 -1
- package/build/publish.d.ts +79 -22
- package/build/publish.js +89 -313
- package/build/publish.js.map +1 -1
- package/build/search.js.map +1 -1
- package/build/star.js +53 -54
- package/build/star.js.map +1 -1
- package/build/stars.js +6 -6
- package/build/stars.js.map +1 -1
- package/build/user.js +19 -1
- package/build/user.js.map +1 -1
- package/build/v1/profile.js.map +1 -1
- package/build/v1/search.js +2 -4
- package/build/v1/search.js.map +1 -1
- package/build/v1/token.js.map +1 -1
- package/build/whoami.js +11 -25
- package/build/whoami.js.map +1 -1
- package/jest.config.js +8 -1
- package/package.json +71 -69
- package/src/dist-tags.ts +63 -50
- package/src/index.ts +1 -6
- package/src/package.ts +37 -44
- package/src/publish.ts +115 -336
- package/src/star.ts +53 -52
- package/src/stars.ts +9 -11
- package/src/user.ts +19 -2
- package/src/v1/search.ts +3 -3
- package/src/whoami.ts +8 -24
- package/test/integration/_helper.ts +97 -54
- package/test/integration/config/distTag.yaml +25 -0
- package/test/integration/config/package.yaml +3 -8
- package/test/integration/config/ping.yaml +2 -6
- package/test/integration/config/publish.yaml +4 -10
- package/test/integration/config/search.yaml +29 -0
- package/test/integration/config/token.jwt.yaml +27 -0
- package/test/integration/config/token.yaml +19 -0
- package/test/integration/config/user.jwt.yaml +37 -0
- package/test/integration/config/user.yaml +8 -14
- package/test/integration/config/whoami.yaml +4 -11
- package/test/integration/distTag.spec.ts +76 -0
- package/test/integration/package.spec.ts +53 -75
- package/test/integration/ping.spec.ts +4 -3
- package/test/integration/publish.spec.ts +60 -25
- package/test/integration/search.spec.ts +126 -0
- package/test/integration/token.spec.ts +124 -0
- package/test/integration/user.jwt.spec.ts +87 -0
- package/test/integration/user.spec.ts +1 -0
- package/test/integration/whoami.spec.ts +21 -39
- package/test/unit/publish.disabled.ts +252 -0
- package/tsconfig.json +1 -1
- package/__mocks__/@verdaccio/logger/index.js +0 -21
- package/build/utils.d.ts +0 -7
- package/build/utils.js +0 -36
- package/build/utils.js.map +0 -1
- package/src/utils.ts +0 -25
- package/test/unit/__snapshots__/publish.spec.ts.snap +0 -49
- package/test/unit/publish.spec.ts +0 -300
package/src/user.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { createRemoteUser } from '@verdaccio/config';
|
|
|
8
8
|
import { API_ERROR, API_MESSAGE, HTTP_STATUS, errorUtils } from '@verdaccio/core';
|
|
9
9
|
import { logger } from '@verdaccio/logger';
|
|
10
10
|
import { Config, RemoteUser } from '@verdaccio/types';
|
|
11
|
-
import { getAuthenticatedMessage, validatePassword } from '@verdaccio/utils';
|
|
11
|
+
import { getAuthenticatedMessage, mask, validatePassword } from '@verdaccio/utils';
|
|
12
12
|
|
|
13
13
|
import { $NextFunctionVer, $RequestExtend } from '../types/custom';
|
|
14
14
|
|
|
@@ -28,6 +28,23 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
28
28
|
}
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* body example
|
|
34
|
+
* req.body = {
|
|
35
|
+
_id: "org.couchdb.user:jjjj",
|
|
36
|
+
name: "jjjj",
|
|
37
|
+
password: "jjjj",
|
|
38
|
+
type: "user",
|
|
39
|
+
roles: [],
|
|
40
|
+
date: "2022-07-08T15:51:04.002Z",
|
|
41
|
+
}
|
|
42
|
+
*
|
|
43
|
+
* @export
|
|
44
|
+
* @param {Router} route
|
|
45
|
+
* @param {IAuth} auth
|
|
46
|
+
* @param {Config} config
|
|
47
|
+
*/
|
|
31
48
|
route.put(
|
|
32
49
|
'/-/user/:org_couchdb_user/:_rev?/:revision?',
|
|
33
50
|
function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
|
@@ -92,7 +109,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
92
109
|
|
|
93
110
|
const token =
|
|
94
111
|
name && password ? await getApiToken(auth, config, user, password) : undefined;
|
|
95
|
-
debug('adduser: new token %o', token);
|
|
112
|
+
debug('adduser: new token %o', mask(token as string, 4));
|
|
96
113
|
if (!token) {
|
|
97
114
|
return next(errorUtils.getUnauthorized());
|
|
98
115
|
}
|
package/src/v1/search.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { IAuth } from '@verdaccio/auth';
|
|
|
5
5
|
import { HTTP_STATUS, searchUtils } from '@verdaccio/core';
|
|
6
6
|
import { logger } from '@verdaccio/logger';
|
|
7
7
|
import { Storage } from '@verdaccio/store';
|
|
8
|
-
import {
|
|
8
|
+
import { Manifest } from '@verdaccio/types';
|
|
9
9
|
|
|
10
10
|
const debug = buildDebug('verdaccio:api:search');
|
|
11
11
|
|
|
@@ -16,7 +16,7 @@ const debug = buildDebug('verdaccio:api:search');
|
|
|
16
16
|
* req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'
|
|
17
17
|
*/
|
|
18
18
|
export default function (route, auth: IAuth, storage: Storage): void {
|
|
19
|
-
function checkAccess(pkg: any, auth: any, remoteUser): Promise<
|
|
19
|
+
function checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {
|
|
20
20
|
return new Promise((resolve, reject) => {
|
|
21
21
|
auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {
|
|
22
22
|
if (err) {
|
|
@@ -49,7 +49,7 @@ export default function (route, auth: IAuth, storage: Storage): void {
|
|
|
49
49
|
from = parseInt(from, 10) || 0;
|
|
50
50
|
|
|
51
51
|
try {
|
|
52
|
-
data = await storage.
|
|
52
|
+
data = await storage.search({
|
|
53
53
|
query,
|
|
54
54
|
url,
|
|
55
55
|
abort,
|
package/src/whoami.ts
CHANGED
|
@@ -1,36 +1,20 @@
|
|
|
1
1
|
import buildDebug from 'debug';
|
|
2
2
|
import { Response, Router } from 'express';
|
|
3
3
|
|
|
4
|
+
import { errorUtils } from '@verdaccio/core';
|
|
5
|
+
|
|
4
6
|
import { $NextFunctionVer, $RequestExtend } from '../types/custom';
|
|
5
7
|
|
|
6
8
|
const debug = buildDebug('verdaccio:api:user');
|
|
7
9
|
|
|
8
10
|
export default function (route: Router): void {
|
|
9
|
-
route.get('
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// debug('whoami: user not found');
|
|
16
|
-
// return next(getUnauthorized('Unauthorized'));
|
|
17
|
-
// }
|
|
18
|
-
debug('whoami: logged by user');
|
|
19
|
-
return next({ username: username });
|
|
20
|
-
} else {
|
|
21
|
-
debug('whoami: redirect next route');
|
|
22
|
-
// redirect to the route below
|
|
23
|
-
return next('route');
|
|
11
|
+
route.get('/-/whoami', (req: $RequestExtend, _res: Response, next: $NextFunctionVer): any => {
|
|
12
|
+
// remote_user is set by the auth middleware
|
|
13
|
+
const username = req?.remote_user?.name;
|
|
14
|
+
if (!username) {
|
|
15
|
+
debug('whoami: user not found');
|
|
16
|
+
return next(errorUtils.getUnauthorized('Unauthorized'));
|
|
24
17
|
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
route.get('/-/whoami', (req: $RequestExtend, res: Response, next: $NextFunctionVer): any => {
|
|
28
|
-
const username = req.remote_user.name;
|
|
29
|
-
// FIXME: this service should return 401 if user missing
|
|
30
|
-
// if (!username) {
|
|
31
|
-
// debug('whoami: user not found');
|
|
32
|
-
// return next(getUnauthorized('Unauthorized'));
|
|
33
|
-
// }
|
|
34
18
|
|
|
35
19
|
debug('whoami: response %o', username);
|
|
36
20
|
return next({ username: username });
|
|
@@ -1,83 +1,126 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { Application } from 'express';
|
|
2
|
+
import _ from 'lodash';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import supertest from 'supertest';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { errorReportingMiddleware, final, handleError } from '@verdaccio/middleware';
|
|
6
|
+
import { parseConfigFile } from '@verdaccio/config';
|
|
7
|
+
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
|
8
|
+
import { setup } from '@verdaccio/logger';
|
|
10
9
|
import { Storage } from '@verdaccio/store';
|
|
11
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
generatePackageMetadata,
|
|
12
|
+
initializeServer as initializeServerHelper,
|
|
13
|
+
} from '@verdaccio/test-helper';
|
|
14
|
+
import { GenericBody } from '@verdaccio/types';
|
|
15
|
+
import { buildToken, generateRandomHexString } from '@verdaccio/utils';
|
|
12
16
|
|
|
13
|
-
import
|
|
17
|
+
import apiMiddleware from '../../src';
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
const configPath = path.join(__dirname, 'config', conf);
|
|
19
|
+
setup();
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
export const getConf = (conf) => {
|
|
22
|
+
const configPath = path.join(__dirname, 'config', conf);
|
|
23
|
+
const config = parseConfigFile(configPath);
|
|
24
|
+
// custom config to avoid conflict with other tests
|
|
25
|
+
config.auth.htpasswd.file = `${config.auth.htpasswd.file}-${generateRandomHexString()}`;
|
|
26
|
+
return config;
|
|
19
27
|
};
|
|
20
28
|
|
|
21
|
-
// TODO: replace by @verdaccio/test-helper
|
|
22
29
|
export async function initializeServer(configName): Promise<Application> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
30
|
+
return initializeServerHelper(getConf(configName), [apiMiddleware], Storage);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createUser(app, name: string, password: string): supertest.Test {
|
|
34
|
+
return supertest(app)
|
|
35
|
+
.put(`/-/user/org.couchdb.user:${name}`)
|
|
36
|
+
.send({
|
|
37
|
+
name: name,
|
|
38
|
+
password: password,
|
|
39
|
+
})
|
|
40
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
41
|
+
.expect(HTTP_STATUS.CREATED);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function getNewToken(app: any, credentials: any): Promise<string> {
|
|
45
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
46
|
+
const { token, ok } = response.body;
|
|
47
|
+
expect(ok).toBeDefined();
|
|
48
|
+
expect(token).toBeDefined();
|
|
49
|
+
expect(typeof token).toBe('string');
|
|
50
|
+
return token;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function generateTokenCLI(app, token, payload): Promise<any> {
|
|
54
|
+
return supertest(app)
|
|
55
|
+
.post('/-/npm/v1/tokens')
|
|
56
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
57
|
+
.send(JSON.stringify(payload))
|
|
58
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
|
59
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function deleteTokenCLI(app, token, tokenToDelete): Promise<any> {
|
|
63
|
+
return supertest(app)
|
|
64
|
+
.delete(`/-/npm/v1/tokens/token/${tokenToDelete}`)
|
|
65
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
66
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
|
67
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
68
|
+
.expect(HTTP_STATUS.OK);
|
|
46
69
|
}
|
|
47
70
|
|
|
48
|
-
export function
|
|
49
|
-
|
|
71
|
+
export function publishVersionWithToken(
|
|
72
|
+
app,
|
|
73
|
+
pkgName: string,
|
|
74
|
+
version: string,
|
|
75
|
+
token: string,
|
|
76
|
+
distTags?: GenericBody
|
|
77
|
+
): supertest.Test {
|
|
78
|
+
const pkgMetadata = generatePackageMetadata(pkgName, version, distTags);
|
|
50
79
|
|
|
51
80
|
return supertest(app)
|
|
52
81
|
.put(`/${encodeURIComponent(pkgName)}`)
|
|
53
82
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
83
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
|
54
84
|
.send(JSON.stringify(pkgMetadata))
|
|
55
85
|
.set('accept', HEADERS.GZIP)
|
|
56
|
-
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
|
57
|
-
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
|
86
|
+
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
|
|
58
87
|
}
|
|
59
88
|
|
|
60
|
-
export
|
|
89
|
+
export function publishVersion(
|
|
61
90
|
app,
|
|
62
|
-
configFile,
|
|
63
91
|
pkgName: string,
|
|
64
92
|
version: string,
|
|
65
|
-
|
|
66
|
-
) {
|
|
67
|
-
const pkgMetadata = generatePackageMetadata(pkgName, version,
|
|
68
|
-
[tag]: version,
|
|
69
|
-
});
|
|
93
|
+
distTags?: GenericBody
|
|
94
|
+
): supertest.Test {
|
|
95
|
+
const pkgMetadata = generatePackageMetadata(pkgName, version, distTags);
|
|
70
96
|
|
|
71
97
|
return supertest(app)
|
|
72
|
-
.put(
|
|
73
|
-
`/${encodeURIComponent(pkgName)}/${encodeURIComponent(version)}/-tag/${encodeURIComponent(
|
|
74
|
-
tag
|
|
75
|
-
)}`
|
|
76
|
-
)
|
|
98
|
+
.put(`/${encodeURIComponent(pkgName)}`)
|
|
77
99
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
78
100
|
.send(JSON.stringify(pkgMetadata))
|
|
79
|
-
.expect(HTTP_STATUS.CREATED)
|
|
80
101
|
.set('accept', HEADERS.GZIP)
|
|
81
|
-
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
|
82
|
-
|
|
102
|
+
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getDisTags(app, pkgName) {
|
|
106
|
+
return supertest(app)
|
|
107
|
+
.get(`/-/package/${encodeURIComponent(pkgName)}/dist-tags`)
|
|
108
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
109
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
110
|
+
.expect(HTTP_STATUS.OK);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getPackage(
|
|
114
|
+
app: any,
|
|
115
|
+
token: string,
|
|
116
|
+
pkgName: string,
|
|
117
|
+
statusCode: number = HTTP_STATUS.OK
|
|
118
|
+
): supertest.Test {
|
|
119
|
+
const test = supertest(app).get(`/${pkgName}`);
|
|
120
|
+
|
|
121
|
+
if (_.isNil(token) === false || _.isEmpty(token) === false) {
|
|
122
|
+
test.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return test.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET).expect(statusCode);
|
|
83
126
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
storage: ./storage
|
|
2
|
+
|
|
3
|
+
auth:
|
|
4
|
+
htpasswd:
|
|
5
|
+
file: ./htpasswd-distTag
|
|
6
|
+
|
|
7
|
+
web:
|
|
8
|
+
enable: true
|
|
9
|
+
title: verdaccio
|
|
10
|
+
|
|
11
|
+
publish:
|
|
12
|
+
allow_offline: false
|
|
13
|
+
|
|
14
|
+
uplinks:
|
|
15
|
+
|
|
16
|
+
log: { type: stdout, format: pretty, level: trace }
|
|
17
|
+
|
|
18
|
+
packages:
|
|
19
|
+
'@*/*':
|
|
20
|
+
access: $anonymous
|
|
21
|
+
publish: $anonymous
|
|
22
|
+
'**':
|
|
23
|
+
access: $anonymous
|
|
24
|
+
publish: $anonymous
|
|
25
|
+
_debug: true
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
store:
|
|
2
|
-
memory:
|
|
3
|
-
limit: 1000
|
|
4
|
-
|
|
5
1
|
auth:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
test:
|
|
9
|
-
name: test
|
|
10
|
-
password: test
|
|
11
|
-
|
|
2
|
+
htpasswd:
|
|
3
|
+
file: ./htpasswd-publish
|
|
12
4
|
web:
|
|
13
5
|
enable: true
|
|
14
6
|
title: verdaccio
|
|
@@ -24,7 +16,9 @@ packages:
|
|
|
24
16
|
'@*/*':
|
|
25
17
|
access: $anonymous
|
|
26
18
|
publish: $anonymous
|
|
19
|
+
unpublish: $anonymous
|
|
27
20
|
'**':
|
|
28
21
|
access: $anonymous
|
|
29
22
|
publish: $anonymous
|
|
23
|
+
unpublish: $anonymous
|
|
30
24
|
_debug: true
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
storage: ./storage
|
|
2
|
+
|
|
3
|
+
auth:
|
|
4
|
+
htpasswd:
|
|
5
|
+
file: ./htpasswd-search
|
|
6
|
+
|
|
7
|
+
web:
|
|
8
|
+
enable: true
|
|
9
|
+
title: verdaccio
|
|
10
|
+
|
|
11
|
+
uplinks:
|
|
12
|
+
|
|
13
|
+
log: { type: stdout, format: pretty, level: trace }
|
|
14
|
+
|
|
15
|
+
packages:
|
|
16
|
+
'private-*':
|
|
17
|
+
access: $all
|
|
18
|
+
publish: jota
|
|
19
|
+
'@private/*':
|
|
20
|
+
access: $all
|
|
21
|
+
publish: jota
|
|
22
|
+
'@*/*':
|
|
23
|
+
access: $all
|
|
24
|
+
publish: $authenticated
|
|
25
|
+
'**':
|
|
26
|
+
access: $all
|
|
27
|
+
publish: $authenticated
|
|
28
|
+
|
|
29
|
+
_debug: true
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
security:
|
|
2
|
+
api:
|
|
3
|
+
jwt:
|
|
4
|
+
sign:
|
|
5
|
+
expiresIn: 5m
|
|
6
|
+
# to avoid invalid verification token, more info on JWT page
|
|
7
|
+
notBefore: 0
|
|
8
|
+
|
|
9
|
+
storage: ./storage
|
|
10
|
+
|
|
11
|
+
auth:
|
|
12
|
+
htpasswd:
|
|
13
|
+
file: ./htpasswd
|
|
14
|
+
|
|
15
|
+
packages:
|
|
16
|
+
'@token/*':
|
|
17
|
+
access: $authenticated
|
|
18
|
+
publish: $authenticated
|
|
19
|
+
'only-you-can-publish':
|
|
20
|
+
access: $authenticated
|
|
21
|
+
publish: $authenticated
|
|
22
|
+
|
|
23
|
+
log: { type: stdout, format: pretty, level: debug }
|
|
24
|
+
|
|
25
|
+
## enable token for testing
|
|
26
|
+
flags:
|
|
27
|
+
token: true
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
storage: ./storage
|
|
2
|
+
|
|
3
|
+
auth:
|
|
4
|
+
htpasswd:
|
|
5
|
+
file: ./htpasswd
|
|
6
|
+
|
|
7
|
+
packages:
|
|
8
|
+
'@token/*':
|
|
9
|
+
access: $authenticated
|
|
10
|
+
publish: $authenticated
|
|
11
|
+
'only-you-can-publish':
|
|
12
|
+
access: $authenticated
|
|
13
|
+
publish: $authenticated
|
|
14
|
+
|
|
15
|
+
log: { type: stdout, format: pretty, level: debug }
|
|
16
|
+
|
|
17
|
+
## enable token for testing
|
|
18
|
+
flags:
|
|
19
|
+
token: true
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
storage: ./storage
|
|
2
|
+
plugins: ./plugins
|
|
3
|
+
|
|
4
|
+
web:
|
|
5
|
+
title: Verdaccio
|
|
6
|
+
|
|
7
|
+
auth:
|
|
8
|
+
htpasswd:
|
|
9
|
+
file: ./htpasswd
|
|
10
|
+
|
|
11
|
+
uplinks:
|
|
12
|
+
ver:
|
|
13
|
+
url: https://registry.verdaccio.org
|
|
14
|
+
|
|
15
|
+
security:
|
|
16
|
+
api:
|
|
17
|
+
jwt:
|
|
18
|
+
sign:
|
|
19
|
+
expiresIn: 10m
|
|
20
|
+
notBefore: 0
|
|
21
|
+
packages:
|
|
22
|
+
'@*/*':
|
|
23
|
+
access: $all
|
|
24
|
+
publish: $authenticated
|
|
25
|
+
'vue':
|
|
26
|
+
access: $authenticated
|
|
27
|
+
publish: $authenticated
|
|
28
|
+
proxy: ver
|
|
29
|
+
'**':
|
|
30
|
+
access: $all
|
|
31
|
+
publish: $authenticated
|
|
32
|
+
|
|
33
|
+
middlewares:
|
|
34
|
+
audit:
|
|
35
|
+
enabled: true
|
|
36
|
+
|
|
37
|
+
log: { type: stdout, format: pretty, level: info }
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
store:
|
|
2
|
-
memory:
|
|
3
|
-
limit: 1000
|
|
4
|
-
|
|
5
1
|
auth:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
test:
|
|
9
|
-
name: test
|
|
10
|
-
password: test
|
|
11
|
-
|
|
2
|
+
htpasswd:
|
|
3
|
+
file: ./htpasswd-user
|
|
12
4
|
web:
|
|
13
5
|
enable: true
|
|
14
6
|
title: verdaccio
|
|
15
7
|
|
|
16
8
|
uplinks:
|
|
17
|
-
|
|
18
|
-
url: https://registry.
|
|
9
|
+
ver:
|
|
10
|
+
url: https://registry.verdaccio.org
|
|
19
11
|
|
|
20
12
|
log: { type: stdout, format: pretty, level: trace }
|
|
21
13
|
|
|
@@ -24,13 +16,15 @@ packages:
|
|
|
24
16
|
access: $all
|
|
25
17
|
publish: $all
|
|
26
18
|
unpublish: $all
|
|
27
|
-
proxy: npmjs
|
|
28
19
|
'verdaccio':
|
|
29
20
|
access: $all
|
|
30
21
|
publish: $all
|
|
22
|
+
'vue':
|
|
23
|
+
access: $authenticated
|
|
24
|
+
publish: $authenticated
|
|
25
|
+
proxy: ver
|
|
31
26
|
'**':
|
|
32
27
|
access: $all
|
|
33
28
|
publish: $all
|
|
34
29
|
unpublish: $all
|
|
35
|
-
proxy: npmjs
|
|
36
30
|
_debug: true
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
store:
|
|
2
|
-
memory:
|
|
3
|
-
limit: 1000
|
|
4
|
-
|
|
5
|
-
auth:
|
|
6
|
-
auth-memory:
|
|
7
|
-
users:
|
|
8
|
-
test:
|
|
9
|
-
name: test
|
|
10
|
-
password: test
|
|
11
|
-
|
|
12
1
|
web:
|
|
13
2
|
enable: true
|
|
14
3
|
title: verdaccio
|
|
@@ -19,6 +8,10 @@ uplinks:
|
|
|
19
8
|
|
|
20
9
|
log: { type: stdout, format: pretty, level: trace }
|
|
21
10
|
|
|
11
|
+
auth:
|
|
12
|
+
htpasswd:
|
|
13
|
+
file: ./htpasswd-whoami
|
|
14
|
+
|
|
22
15
|
packages:
|
|
23
16
|
'@*/*':
|
|
24
17
|
access: $all
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import supertest from 'supertest';
|
|
2
|
+
|
|
3
|
+
import { API_MESSAGE, HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
|
|
4
|
+
|
|
5
|
+
import { getDisTags, initializeServer, publishVersion } from './_helper';
|
|
6
|
+
|
|
7
|
+
describe('package', () => {
|
|
8
|
+
let app;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
app = await initializeServer('distTag.yaml');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test.each([['foo'], ['@scope/foo']])('should display dist-tag (npm dist-tag ls)', async (pkg) => {
|
|
14
|
+
await publishVersion(app, pkg, '1.0.0');
|
|
15
|
+
await publishVersion(app, pkg, '1.0.1');
|
|
16
|
+
const response = await getDisTags(app, pkg);
|
|
17
|
+
expect(response.body).toEqual({ latest: '1.0.1' });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should add a version to a tag (npm dist-tag add)', async () => {
|
|
21
|
+
await publishVersion(app, encodeURIComponent('foo'), '1.0.0');
|
|
22
|
+
await publishVersion(app, encodeURIComponent('foo'), '1.0.1');
|
|
23
|
+
|
|
24
|
+
const response = await supertest(app)
|
|
25
|
+
.put(`/${encodeURIComponent('foo')}/test`)
|
|
26
|
+
.set(HEADERS.ACCEPT, HEADERS.GZIP)
|
|
27
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
28
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
29
|
+
.send(JSON.stringify('1.0.1'))
|
|
30
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
31
|
+
.expect(HTTP_STATUS.CREATED);
|
|
32
|
+
expect(response.body.ok).toEqual(API_MESSAGE.TAG_ADDED);
|
|
33
|
+
const response2 = await getDisTags(app, 'foo');
|
|
34
|
+
expect(response2.body).toEqual({ latest: '1.0.1', test: '1.0.1' });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should fails if a version is missing (npm dist-tag add)', async () => {
|
|
38
|
+
await publishVersion(app, encodeURIComponent('foo'), '1.0.0');
|
|
39
|
+
await publishVersion(app, encodeURIComponent('foo'), '1.0.1');
|
|
40
|
+
|
|
41
|
+
await supertest(app)
|
|
42
|
+
.put(`/${encodeURIComponent('foo')}/test`)
|
|
43
|
+
.set(HEADERS.ACCEPT, HEADERS.GZIP)
|
|
44
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
45
|
+
.send(JSON.stringify({}))
|
|
46
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
47
|
+
.expect(HTTP_STATUS.BAD_REQUEST);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('should delete a previous added tag (npm dist-tag rm)', async () => {
|
|
51
|
+
await publishVersion(app, encodeURIComponent('foo'), '1.0.0');
|
|
52
|
+
await publishVersion(app, encodeURIComponent('foo'), '1.0.1');
|
|
53
|
+
|
|
54
|
+
const response = await supertest(app)
|
|
55
|
+
.put(`/${encodeURIComponent('foo')}/beta`)
|
|
56
|
+
.set(HEADERS.ACCEPT, HEADERS.GZIP)
|
|
57
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
58
|
+
.send(JSON.stringify('1.0.1'))
|
|
59
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
60
|
+
.expect(HTTP_STATUS.CREATED);
|
|
61
|
+
expect(response.body.ok).toEqual(API_MESSAGE.TAG_ADDED);
|
|
62
|
+
|
|
63
|
+
const response2 = await getDisTags(app, 'foo');
|
|
64
|
+
expect(response2.body).toEqual({ latest: '1.0.1', beta: '1.0.1' });
|
|
65
|
+
|
|
66
|
+
const response3 = await supertest(app)
|
|
67
|
+
.delete(`/-/package/${encodeURIComponent('foo')}/dist-tags/beta`)
|
|
68
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
69
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
70
|
+
.expect(HTTP_STATUS.CREATED);
|
|
71
|
+
expect(response3.body.ok).toEqual(API_MESSAGE.TAG_REMOVED);
|
|
72
|
+
|
|
73
|
+
const response4 = await getDisTags(app, 'foo');
|
|
74
|
+
expect(response4.body).toEqual({ latest: '1.0.1' });
|
|
75
|
+
});
|
|
76
|
+
});
|