@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/build/dist-tags.js +40 -35
  3. package/build/dist-tags.js.map +1 -1
  4. package/build/index.js +1 -7
  5. package/build/index.js.map +1 -1
  6. package/build/package.js +39 -28
  7. package/build/package.js.map +1 -1
  8. package/build/ping.js.map +1 -1
  9. package/build/publish.d.ts +79 -22
  10. package/build/publish.js +89 -313
  11. package/build/publish.js.map +1 -1
  12. package/build/search.js.map +1 -1
  13. package/build/star.js +53 -54
  14. package/build/star.js.map +1 -1
  15. package/build/stars.js +6 -6
  16. package/build/stars.js.map +1 -1
  17. package/build/user.js +19 -1
  18. package/build/user.js.map +1 -1
  19. package/build/v1/profile.js.map +1 -1
  20. package/build/v1/search.js +2 -4
  21. package/build/v1/search.js.map +1 -1
  22. package/build/v1/token.js.map +1 -1
  23. package/build/whoami.js +11 -25
  24. package/build/whoami.js.map +1 -1
  25. package/jest.config.js +8 -1
  26. package/package.json +71 -69
  27. package/src/dist-tags.ts +63 -50
  28. package/src/index.ts +1 -6
  29. package/src/package.ts +37 -44
  30. package/src/publish.ts +115 -336
  31. package/src/star.ts +53 -52
  32. package/src/stars.ts +9 -11
  33. package/src/user.ts +19 -2
  34. package/src/v1/search.ts +3 -3
  35. package/src/whoami.ts +8 -24
  36. package/test/integration/_helper.ts +97 -54
  37. package/test/integration/config/distTag.yaml +25 -0
  38. package/test/integration/config/package.yaml +3 -8
  39. package/test/integration/config/ping.yaml +2 -6
  40. package/test/integration/config/publish.yaml +4 -10
  41. package/test/integration/config/search.yaml +29 -0
  42. package/test/integration/config/token.jwt.yaml +27 -0
  43. package/test/integration/config/token.yaml +19 -0
  44. package/test/integration/config/user.jwt.yaml +37 -0
  45. package/test/integration/config/user.yaml +8 -14
  46. package/test/integration/config/whoami.yaml +4 -11
  47. package/test/integration/distTag.spec.ts +76 -0
  48. package/test/integration/package.spec.ts +53 -75
  49. package/test/integration/ping.spec.ts +4 -3
  50. package/test/integration/publish.spec.ts +60 -25
  51. package/test/integration/search.spec.ts +126 -0
  52. package/test/integration/token.spec.ts +124 -0
  53. package/test/integration/user.jwt.spec.ts +87 -0
  54. package/test/integration/user.spec.ts +1 -0
  55. package/test/integration/whoami.spec.ts +21 -39
  56. package/test/unit/publish.disabled.ts +252 -0
  57. package/tsconfig.json +1 -1
  58. package/__mocks__/@verdaccio/logger/index.js +0 -21
  59. package/build/utils.d.ts +0 -7
  60. package/build/utils.js +0 -36
  61. package/build/utils.js.map +0 -1
  62. package/src/utils.ts +0 -25
  63. package/test/unit/__snapshots__/publish.spec.ts.snap +0 -49
  64. package/test/unit/publish.spec.ts +0 -300
package/package.json CHANGED
@@ -1,70 +1,72 @@
1
1
  {
2
- "name": "@verdaccio/api",
3
- "version": "6.0.0-6-next.25",
4
- "description": "loaders logic",
5
- "main": "./build/index.js",
6
- "types": "build/index.d.ts",
7
- "author": {
8
- "name": "Juan Picado",
9
- "email": "juanpicado19@gmail.com"
10
- },
11
- "repository": {
12
- "type": "https",
13
- "url": "https://github.com/verdaccio/verdaccio"
14
- },
15
- "homepage": "https://verdaccio.org",
16
- "keywords": [
17
- "private",
18
- "package",
19
- "repository",
20
- "registry",
21
- "enterprise",
22
- "modules",
23
- "proxy",
24
- "server",
25
- "verdaccio"
26
- ],
27
- "engines": {
28
- "node": ">=14",
29
- "npm": ">=6"
30
- },
31
- "license": "MIT",
32
- "dependencies": {
33
- "@verdaccio/auth": "6.0.0-6-next.22",
34
- "@verdaccio/config": "6.0.0-6-next.14",
35
- "@verdaccio/core": "6.0.0-6-next.5",
36
- "@verdaccio/hooks": "6.0.0-6-next.13",
37
- "@verdaccio/logger": "6.0.0-6-next.11",
38
- "@verdaccio/middleware": "6.0.0-6-next.22",
39
- "@verdaccio/store": "6.0.0-6-next.22",
40
- "@verdaccio/utils": "6.0.0-6-next.11",
41
- "abortcontroller-polyfill": "1.7.3",
42
- "cookies": "0.8.0",
43
- "debug": "4.3.3",
44
- "body-parser": "1.19.1",
45
- "express": "4.17.2",
46
- "lodash": "4.17.21",
47
- "mime": "2.6.0",
48
- "semver": "7.3.5"
49
- },
50
- "devDependencies": {
51
- "@types/node": "16.11.21",
52
- "@verdaccio/server": "6.0.0-6-next.30",
53
- "@verdaccio/types": "11.0.0-6-next.12",
54
- "@verdaccio/test-helper": "1.1.0-6-next.0",
55
- "supertest": "6.2.2"
56
- },
57
- "funding": {
58
- "type": "opencollective",
59
- "url": "https://opencollective.com/verdaccio"
60
- },
61
- "scripts": {
62
- "clean": "rimraf ./build",
63
- "test": "cross-env NODE_ENV=test BABEL_ENV=test jest",
64
- "type-check": "tsc --noEmit -p tsconfig.build.json",
65
- "build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
66
- "build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
67
- "watch": "pnpm build:js -- --watch",
68
- "build": "pnpm run build:js && pnpm run build:types"
69
- }
70
- }
2
+ "name": "@verdaccio/api",
3
+ "version": "6.0.0-6-next.28",
4
+ "description": "loaders logic",
5
+ "main": "./build/index.js",
6
+ "types": "build/index.d.ts",
7
+ "author": {
8
+ "name": "Juan Picado",
9
+ "email": "juanpicado19@gmail.com"
10
+ },
11
+ "repository": {
12
+ "type": "https",
13
+ "url": "https://github.com/verdaccio/verdaccio"
14
+ },
15
+ "homepage": "https://verdaccio.org",
16
+ "keywords": [
17
+ "private",
18
+ "package",
19
+ "repository",
20
+ "registry",
21
+ "enterprise",
22
+ "modules",
23
+ "proxy",
24
+ "server",
25
+ "verdaccio"
26
+ ],
27
+ "engines": {
28
+ "node": ">=14",
29
+ "npm": ">=6"
30
+ },
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@verdaccio/auth": "6.0.0-6-next.24",
34
+ "@verdaccio/config": "6.0.0-6-next.16",
35
+ "@verdaccio/core": "6.0.0-6-next.7",
36
+ "@verdaccio/logger": "6.0.0-6-next.13",
37
+ "@verdaccio/middleware": "6.0.0-6-next.24",
38
+ "@verdaccio/store": "6.0.0-6-next.25",
39
+ "@verdaccio/utils": "6.0.0-6-next.13",
40
+ "abortcontroller-polyfill": "1.7.3",
41
+ "cookies": "0.8.0",
42
+ "debug": "4.3.4",
43
+ "body-parser": "1.20.0",
44
+ "express": "4.18.1",
45
+ "lodash": "4.17.21",
46
+ "mime": "2.6.0",
47
+ "semver": "7.3.7"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "16.11.56",
51
+ "@verdaccio/server": "6.0.0-6-next.34",
52
+ "@verdaccio/types": "11.0.0-6-next.15",
53
+ "@verdaccio/test-helper": "1.1.0-6-next.3",
54
+ "supertest": "6.2.4",
55
+ "nock": "13.2.9",
56
+ "mockdate": "3.0.5"
57
+ },
58
+ "funding": {
59
+ "type": "opencollective",
60
+ "url": "https://opencollective.com/verdaccio"
61
+ },
62
+ "scripts": {
63
+ "clean": "rimraf ./build",
64
+ "test": "jest",
65
+ "type-check": "tsc --noEmit -p tsconfig.build.json",
66
+ "build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
67
+ "build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
68
+ "watch": "pnpm build:js -- --watch",
69
+ "build": "pnpm run build:js && pnpm run build:types"
70
+ },
71
+ "readme": "# @verdaccio/api\n\n[![backers](https://opencollective.com/verdaccio/tiers/backer/badge.svg?label=Backer&color=brightgreen)](https://opencollective.com/verdaccio)\n[![stackshare](https://img.shields.io/badge/Follow%20on-StackShare-blue.svg?logo=stackshare&style=flat)](https://stackshare.io/verdaccio)\n[![MIT](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)\n[![Crowdin](https://d322cqt584bo4o.cloudfront.net/verdaccio/localized.svg)](https://crowdin.com/project/verdaccio)\n[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/verdaccio/verdaccio)](https://www.tickgit.com/browse?repo=github.com/verdaccio/verdaccio)\n\n[![Twitter followers](https://img.shields.io/twitter/follow/verdaccio_npm.svg?style=social&label=Follow)](https://twitter.com/verdaccio_npm)\n[![Github](https://img.shields.io/github/stars/verdaccio/verdaccio.svg?style=social&label=Stars)](https://github.com/verdaccio/verdaccio/stargazers)\n\n## Donations\n\nVerdaccio 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.** 😉\n\n**[Donate](https://opencollective.com/verdaccio)** 💵👍🏻 starting from _\\$1/month_ or just one single contribution.\n\n## Report a vulnerability\n\nIf 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).\n\n## Open Collective Sponsors\n\nSupport 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#sponsor)]\n\n[![sponsor](https://opencollective.com/verdaccio/sponsor/0/avatar.svg)](https://opencollective.com/verdaccio/sponsor/0/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/1/avatar.svg)](https://opencollective.com/verdaccio/sponsor/1/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/2/avatar.svg)](https://opencollective.com/verdaccio/sponsor/2/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/3/avatar.svg)](https://opencollective.com/verdaccio/sponsor/3/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/4/avatar.svg)](https://opencollective.com/verdaccio/sponsor/4/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/5/avatar.svg)](https://opencollective.com/verdaccio/sponsor/5/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/6/avatar.svg)](https://opencollective.com/verdaccio/sponsor/6/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/7/avatar.svg)](https://opencollective.com/verdaccio/sponsor/7/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/8/avatar.svg)](https://opencollective.com/verdaccio/sponsor/8/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/9/avatar.svg)](https://opencollective.com/verdaccio/sponsor/9/website)\n\n## Open Collective Backers\n\nThank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/verdaccio#backer)]\n\n[![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio#backers)\n\n## Special Thanks\n\nThanks to the following companies to help us to achieve our goals providing free open source licenses.\n"
72
+ }
package/src/dist-tags.ts CHANGED
@@ -3,106 +3,119 @@ import _ from 'lodash';
3
3
  import mime from 'mime';
4
4
 
5
5
  import { IAuth } from '@verdaccio/auth';
6
- import { VerdaccioError, constants } from '@verdaccio/core';
6
+ import { constants, errorUtils } from '@verdaccio/core';
7
7
  import { allow, media } from '@verdaccio/middleware';
8
8
  import { Storage } from '@verdaccio/store';
9
- import { Package } from '@verdaccio/types';
10
9
 
11
10
  import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';
12
11
 
13
12
  export default function (route: Router, auth: IAuth, storage: Storage): void {
14
13
  const can = allow(auth);
15
- const tag_package_version = function (
14
+ const addTagPackageVersionMiddleware = async function (
16
15
  req: $RequestExtend,
17
16
  res: $ResponseExtend,
18
17
  next: $NextFunctionVer
19
- ): $NextFunctionVer {
18
+ ): Promise<$NextFunctionVer> {
20
19
  if (_.isString(req.body) === false) {
21
- return next('route');
20
+ return next(errorUtils.getBadRequest('version is missing'));
22
21
  }
23
22
 
24
23
  const tags = {};
25
24
  tags[req.params.tag] = req.body;
26
- storage.mergeTags(req.params.package, tags, function (err: Error): $NextFunctionVer {
27
- if (err) {
28
- return next(err);
29
- }
25
+ try {
26
+ await storage.mergeTagsNext(req.params.package, tags);
30
27
  res.status(constants.HTTP_STATUS.CREATED);
31
- return next({ ok: constants.API_MESSAGE.TAG_ADDED });
32
- });
28
+ return next({
29
+ ok: constants.API_MESSAGE.TAG_ADDED,
30
+ });
31
+ } catch (err) {
32
+ next(err);
33
+ }
33
34
  };
34
35
 
35
36
  // tagging a package.
36
- route.put('/:package/:tag', can('publish'), media(mime.getType('json')), tag_package_version);
37
-
38
- route.post(
39
- '/-/package/:package/dist-tags/:tag',
37
+ route.put(
38
+ '/:package/:tag',
40
39
  can('publish'),
41
40
  media(mime.getType('json')),
42
- tag_package_version
41
+ addTagPackageVersionMiddleware
43
42
  );
44
43
 
45
44
  route.put(
46
45
  '/-/package/:package/dist-tags/:tag',
47
46
  can('publish'),
48
47
  media(mime.getType('json')),
49
- tag_package_version
48
+ addTagPackageVersionMiddleware
50
49
  );
51
50
 
52
51
  route.delete(
53
52
  '/-/package/:package/dist-tags/:tag',
54
53
  can('publish'),
55
- function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
54
+ async function (
55
+ req: $RequestExtend,
56
+ res: $ResponseExtend,
57
+ next: $NextFunctionVer
58
+ ): Promise<void> {
56
59
  const tags = {};
57
60
  tags[req.params.tag] = null;
58
- storage.mergeTags(req.params.package, tags, function (err: VerdaccioError): $NextFunctionVer {
59
- if (err) {
60
- return next(err);
61
- }
61
+ try {
62
+ await storage.mergeTagsNext(req.params.package, tags);
62
63
  res.status(constants.HTTP_STATUS.CREATED);
63
64
  return next({
64
65
  ok: constants.API_MESSAGE.TAG_REMOVED,
65
66
  });
66
- });
67
+ } catch (err) {
68
+ next(err);
69
+ }
67
70
  }
68
71
  );
69
72
 
70
73
  route.get(
71
74
  '/-/package/:package/dist-tags',
72
75
  can('access'),
73
- function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
74
- storage.getPackage({
75
- name: req.params.package,
76
- uplinksLook: true,
77
- req,
78
- callback: function (err: VerdaccioError, info: Package): $NextFunctionVer {
79
- if (err) {
80
- return next(err);
81
- }
82
-
83
- next(info[constants.DIST_TAGS]);
84
- },
85
- });
76
+ async function (
77
+ req: $RequestExtend,
78
+ res: $ResponseExtend,
79
+ next: $NextFunctionVer
80
+ ): Promise<void> {
81
+ const name = req.params.package;
82
+ const requestOptions = {
83
+ protocol: req.protocol,
84
+ headers: req.headers as any,
85
+ // FIXME: if we migrate to req.hostname, the port is not longer included.
86
+ host: req.host,
87
+ remoteAddress: req.socket.remoteAddress,
88
+ };
89
+ try {
90
+ const manifest = await storage.getPackageByOptions({
91
+ name,
92
+ uplinksLook: true,
93
+ requestOptions,
94
+ });
95
+ next(manifest[constants.DIST_TAGS]);
96
+ } catch (err) {
97
+ next(err);
98
+ }
86
99
  }
87
100
  );
88
101
 
89
102
  route.post(
90
103
  '/-/package/:package/dist-tags',
91
104
  can('publish'),
92
- function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
93
- storage.mergeTags(
94
- req.params.package,
95
- req.body,
96
- function (err: VerdaccioError): $NextFunctionVer {
97
- if (err) {
98
- return next(err);
99
- }
100
- res.status(constants.HTTP_STATUS.CREATED);
101
- return next({
102
- ok: constants.API_MESSAGE.TAG_UPDATED,
103
- });
104
- }
105
- );
105
+ async function (
106
+ req: $RequestExtend,
107
+ res: $ResponseExtend,
108
+ next: $NextFunctionVer
109
+ ): Promise<void> {
110
+ try {
111
+ await storage.mergeTagsNext(req.params.package, req.body);
112
+ res.status(constants.HTTP_STATUS.CREATED);
113
+ return next({
114
+ ok: constants.API_MESSAGE.TAG_UPDATED,
115
+ });
116
+ } catch (err) {
117
+ next(err);
118
+ }
106
119
  }
107
120
  );
108
121
  }
package/src/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import bodyParser from 'body-parser';
2
2
  import express, { Router } from 'express';
3
- import semver from 'semver';
4
3
 
5
4
  import { IAuth } from '@verdaccio/auth';
6
5
  import {
@@ -25,10 +24,6 @@ import v1Search from './v1/search';
25
24
  import token from './v1/token';
26
25
  import whoami from './whoami';
27
26
 
28
- if (semver.lte(process.version, 'v15.0.0')) {
29
- global.AbortController = require('abortcontroller-polyfill/dist/cjs-ponyfill').AbortController;
30
- }
31
-
32
27
  export default function (config: Config, auth: IAuth, storage: Storage): Router {
33
28
  /* eslint new-cap:off */
34
29
  const app = express.Router();
@@ -62,7 +57,7 @@ export default function (config: Config, auth: IAuth, storage: Storage): Router
62
57
  search(app);
63
58
  user(app, auth, config);
64
59
  distTags(app, auth, storage);
65
- publish(app, auth, storage, config);
60
+ publish(app, auth, storage);
66
61
  ping(app);
67
62
  stars(app, storage);
68
63
  // @ts-ignore
package/src/package.ts CHANGED
@@ -2,7 +2,7 @@ import buildDebug from 'debug';
2
2
  import { Router } from 'express';
3
3
 
4
4
  import { IAuth } from '@verdaccio/auth';
5
- import { HEADERS, errorUtils } from '@verdaccio/core';
5
+ import { HEADERS, HEADER_TYPE } from '@verdaccio/core';
6
6
  import { allow } from '@verdaccio/middleware';
7
7
  import { Storage } from '@verdaccio/store';
8
8
 
@@ -10,27 +10,6 @@ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/cust
10
10
 
11
11
  const debug = buildDebug('verdaccio:api:package');
12
12
 
13
- const downloadStream = (
14
- packageName: string,
15
- filename: string,
16
- storage: any,
17
- _req: $RequestExtend,
18
- res: $ResponseExtend
19
- ): void => {
20
- const stream = storage.getTarball(packageName, filename);
21
-
22
- stream.on('content-length', function (content): void {
23
- res.header('Content-Length', content);
24
- });
25
-
26
- stream.on('error', function (err): void {
27
- return res.locals.report_error(err);
28
- });
29
-
30
- res.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);
31
- stream.pipe(res);
32
- };
33
-
34
13
  export default function (route: Router, auth: IAuth, storage: Storage): void {
35
14
  const can = allow(auth);
36
15
 
@@ -44,28 +23,24 @@ export default function (route: Router, auth: IAuth, storage: Storage): void {
44
23
  ): Promise<void> {
45
24
  debug('init package by version');
46
25
  const name = req.params.package;
47
- let queryVersion = req.params.version;
26
+ let version = req.params.version;
27
+ const write = req.query.write === 'true';
28
+ const abbreviated = req.get('Accept') === Storage.ABBREVIATED_HEADER;
48
29
  const requestOptions = {
49
30
  protocol: req.protocol,
50
31
  headers: req.headers as any,
51
32
  // FIXME: if we migrate to req.hostname, the port is not longer included.
52
33
  host: req.host,
34
+ remoteAddress: req.socket.remoteAddress,
35
+ byPassCache: write,
53
36
  };
54
37
 
55
38
  try {
56
- // TODO: this is just temporary while I migrate all plugins to use the new API
57
- // the method will be renamed to getPackage again but Promise Based.
58
- if (!storage.getPackageByOptions) {
59
- throw errorUtils.getInternalError(
60
- 'getPackageByOptions not implemented, check pr-2750 for more details'
61
- );
62
- }
63
-
64
39
  const manifest = await storage.getPackageByOptions({
65
40
  name,
66
41
  uplinksLook: true,
67
- req,
68
- version: queryVersion,
42
+ abbreviated,
43
+ version,
69
44
  requestOptions,
70
45
  });
71
46
  next(manifest);
@@ -76,20 +51,38 @@ export default function (route: Router, auth: IAuth, storage: Storage): void {
76
51
  );
77
52
 
78
53
  route.get(
79
- '/:scopedPackage/-/:scope/:filename',
54
+ '/:pkg/-/:filename',
80
55
  can('access'),
81
- function (req: $RequestExtend, res: $ResponseExtend): void {
82
- const { scopedPackage, filename } = req.params;
56
+ async function (req: $RequestExtend, res: $ResponseExtend, next): Promise<void> {
57
+ const { pkg, filename } = req.params;
58
+ const abort = new AbortController();
59
+ try {
60
+ const stream = (await storage.getTarballNext(pkg, filename, {
61
+ signal: abort.signal,
62
+ // enableRemote: true,
63
+ })) as any;
83
64
 
84
- downloadStream(scopedPackage, filename, storage, req, res);
85
- }
86
- );
65
+ stream.on('content-length', (size) => {
66
+ res.header(HEADER_TYPE.CONTENT_LENGTH, size);
67
+ });
87
68
 
88
- route.get(
89
- '/:package/-/:filename',
90
- can('access'),
91
- function (req: $RequestExtend, res: $ResponseExtend): void {
92
- downloadStream(req.params.package, req.params.filename, storage, req, res);
69
+ stream.once('error', (err) => {
70
+ res.locals.report_error(err);
71
+ next(err);
72
+ });
73
+
74
+ req.on('abort', () => {
75
+ debug('request aborted for %o', req.url);
76
+ abort.abort();
77
+ });
78
+
79
+ res.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);
80
+ stream.pipe(res);
81
+ } catch (err: any) {
82
+ // console.log('catch API error request', err);
83
+ res.locals.report_error(err);
84
+ next(err);
85
+ }
93
86
  }
94
87
  );
95
88
  }