@tramvai/cli 3.26.2 → 3.27.0
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/lib/api/shared/providers/selfSignedCertificateProvider.d.ts +3 -0
- package/lib/api/shared/providers/selfSignedCertificateProvider.d.ts.map +1 -0
- package/lib/api/shared/providers/selfSignedCertificateProvider.js +27 -0
- package/lib/api/shared/providers/selfSignedCertificateProvider.js.map +1 -0
- package/lib/api/shared/types/base.d.ts +4 -0
- package/lib/api/shared/types/base.d.ts.map +1 -1
- package/lib/api/shared/utils/selfSignedCertificate/certificatedHostsList.d.ts +4 -0
- package/lib/api/shared/utils/selfSignedCertificate/certificatedHostsList.d.ts.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/certificatedHostsList.js +22 -0
- package/lib/api/shared/utils/selfSignedCertificate/certificatedHostsList.js.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/common.d.ts +3 -0
- package/lib/api/shared/utils/selfSignedCertificate/common.d.ts.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/common.js +8 -0
- package/lib/api/shared/utils/selfSignedCertificate/common.js.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/createSelfSignedCertificate.d.ts +9 -0
- package/lib/api/shared/utils/selfSignedCertificate/createSelfSignedCertificate.d.ts.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/createSelfSignedCertificate.js +64 -0
- package/lib/api/shared/utils/selfSignedCertificate/createSelfSignedCertificate.js.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/getHosts.d.ts +3 -0
- package/lib/api/shared/utils/selfSignedCertificate/getHosts.d.ts.map +1 -0
- package/lib/api/shared/utils/selfSignedCertificate/getHosts.js +32 -0
- package/lib/api/shared/utils/selfSignedCertificate/getHosts.js.map +1 -0
- package/lib/api/start/index.d.ts +3 -0
- package/lib/api/start/index.d.ts.map +1 -1
- package/lib/api/start/index.js.map +1 -1
- package/lib/api/start/providers/application/server.d.ts.map +1 -1
- package/lib/api/start/providers/application/server.js +14 -3
- package/lib/api/start/providers/application/server.js.map +1 -1
- package/lib/api/start/providers/application/shared.d.ts.map +1 -1
- package/lib/api/start/providers/application/shared.js +11 -1
- package/lib/api/start/providers/application/shared.js.map +1 -1
- package/lib/api/start/utils/banner.d.ts.map +1 -1
- package/lib/api/start/utils/banner.js +8 -2
- package/lib/api/start/utils/banner.js.map +1 -1
- package/lib/api/start/utils/createServer.d.ts +3 -1
- package/lib/api/start/utils/createServer.d.ts.map +1 -1
- package/lib/api/start/utils/createServer.js +16 -1
- package/lib/api/start/utils/createServer.js.map +1 -1
- package/lib/api/start-prod/providers/application.d.ts.map +1 -1
- package/lib/api/start-prod/providers/application.js +24 -3
- package/lib/api/start-prod/providers/application.js.map +1 -1
- package/lib/api/start-prod/providers/child-app.d.ts.map +1 -1
- package/lib/api/start-prod/providers/child-app.js +5 -0
- package/lib/api/start-prod/providers/child-app.js.map +1 -1
- package/lib/api/start-prod/providers/shared.d.ts.map +1 -1
- package/lib/api/start-prod/providers/shared.js +0 -5
- package/lib/api/start-prod/providers/shared.js.map +1 -1
- package/lib/builder/webpack/devServer/server.d.ts.map +1 -1
- package/lib/builder/webpack/devServer/server.js +6 -1
- package/lib/builder/webpack/devServer/server.js.map +1 -1
- package/lib/builder/webpack/tokens.d.ts +3 -0
- package/lib/builder/webpack/tokens.d.ts.map +1 -1
- package/lib/commands/start/command.d.ts.map +1 -1
- package/lib/commands/start/command.js +16 -0
- package/lib/commands/start/command.js.map +1 -1
- package/lib/commands/start-prod/command.d.ts.map +1 -1
- package/lib/commands/start-prod/command.js +16 -0
- package/lib/commands/start-prod/command.js.map +1 -1
- package/lib/commands/static/application.d.ts.map +1 -1
- package/lib/commands/static/application.js +1 -1
- package/lib/commands/static/application.js.map +1 -1
- package/lib/config/configManager.d.ts +2 -0
- package/lib/config/configManager.d.ts.map +1 -1
- package/lib/config/configManager.js +2 -2
- package/lib/config/configManager.js.map +1 -1
- package/lib/di/tokens/config.d.ts +1 -0
- package/lib/di/tokens/config.d.ts.map +1 -1
- package/lib/di/tokens/server.d.ts +6 -0
- package/lib/di/tokens/server.d.ts.map +1 -1
- package/lib/di/tokens/server.js +2 -1
- package/lib/di/tokens/server.js.map +1 -1
- package/lib/library/webpack/application/client/dev.d.ts.map +1 -1
- package/lib/library/webpack/application/client/dev.js +6 -1
- package/lib/library/webpack/application/client/dev.js.map +1 -1
- package/lib/library/webpack/application/server/dev.d.ts.map +1 -1
- package/lib/library/webpack/application/server/dev.js +6 -1
- package/lib/library/webpack/application/server/dev.js.map +1 -1
- package/lib/schema/autogeneratedSchema.json +15 -15
- package/lib/utils/getApplicationUrl.d.ts +6 -0
- package/lib/utils/getApplicationUrl.d.ts.map +1 -0
- package/lib/utils/getApplicationUrl.js +8 -0
- package/lib/utils/getApplicationUrl.js.map +1 -0
- package/package.json +3 -3
- package/schema.json +15 -15
- package/src/api/shared/providers/selfSignedCertificateProvider.ts +29 -0
- package/src/api/shared/types/base.ts +5 -0
- package/src/api/shared/utils/selfSignedCertificate/certificatedHostsList.ts +22 -0
- package/src/api/shared/utils/selfSignedCertificate/common.ts +4 -0
- package/src/api/shared/utils/selfSignedCertificate/createSelfSignedCertificate.spec.ts +154 -0
- package/src/api/shared/utils/selfSignedCertificate/createSelfSignedCertificate.ts +99 -0
- package/src/api/shared/utils/selfSignedCertificate/getHosts.ts +31 -0
- package/src/api/start/index.ts +3 -0
- package/src/api/start/providers/application/server.ts +21 -5
- package/src/api/start/providers/application/shared.ts +12 -2
- package/src/api/start/utils/banner.ts +12 -4
- package/src/api/start/utils/createServer.ts +19 -1
- package/src/api/start-prod/providers/application.ts +27 -3
- package/src/api/start-prod/providers/child-app.ts +6 -0
- package/src/api/start-prod/providers/shared.ts +0 -5
- package/src/builder/webpack/devServer/server.ts +6 -1
- package/src/commands/start/command.ts +16 -0
- package/src/commands/start-prod/command.ts +16 -0
- package/src/commands/static/application.ts +3 -1
- package/src/config/configManager.ts +7 -4
- package/src/di/tokens/server.ts +5 -0
- package/src/library/webpack/application/client/dev.ts +6 -3
- package/src/library/webpack/application/server/dev.ts +6 -3
- package/src/schema/autogeneratedSchema.json +15 -15
- package/src/utils/getApplicationUrl.ts +11 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getApplicationUrl = void 0;
|
|
4
|
+
const getApplicationUrl = ({ protocol, host, port, }) => {
|
|
5
|
+
return `${protocol}://${host}:${port}`;
|
|
6
|
+
};
|
|
7
|
+
exports.getApplicationUrl = getApplicationUrl;
|
|
8
|
+
//# sourceMappingURL=getApplicationUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getApplicationUrl.js","sourceRoot":"","sources":["../../src/utils/getApplicationUrl.ts"],"names":[],"mappings":";;;AAAO,MAAM,iBAAiB,GAAG,CAAC,EAChC,QAAQ,EACR,IAAI,EACJ,IAAI,GAKL,EAAE,EAAE;IACH,OAAO,GAAG,QAAQ,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;AACzC,CAAC,CAAC;AAVW,QAAA,iBAAiB,qBAU5B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.27.0",
|
|
4
4
|
"description": "Cli инструмент для сборки и запуска приложений",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"@tinkoff/request-plugin-protocol-http": "^0.11.8",
|
|
71
71
|
"@tinkoff/utils": "^2.1.3",
|
|
72
72
|
"@tinkoff/webpack-dedupe-plugin": "2.0.2",
|
|
73
|
-
"@tramvai/build": "4.1.
|
|
74
|
-
"@tramvai/react": "3.
|
|
73
|
+
"@tramvai/build": "4.1.1",
|
|
74
|
+
"@tramvai/react": "3.27.0",
|
|
75
75
|
"@tramvai/tools-check-versions": "0.5.3",
|
|
76
76
|
"@tramvai/tools-migrate": "0.7.3",
|
|
77
77
|
"ajv": "^8.12.0",
|
package/schema.json
CHANGED
|
@@ -1166,23 +1166,23 @@
|
|
|
1166
1166
|
"dotAll": {
|
|
1167
1167
|
"type": "boolean"
|
|
1168
1168
|
},
|
|
1169
|
-
"__@match@
|
|
1169
|
+
"__@match@6855": {
|
|
1170
1170
|
"type": "object",
|
|
1171
1171
|
"additionalProperties": false
|
|
1172
1172
|
},
|
|
1173
|
-
"__@replace@
|
|
1173
|
+
"__@replace@6857": {
|
|
1174
1174
|
"type": "object",
|
|
1175
1175
|
"additionalProperties": false
|
|
1176
1176
|
},
|
|
1177
|
-
"__@search@
|
|
1177
|
+
"__@search@6860": {
|
|
1178
1178
|
"type": "object",
|
|
1179
1179
|
"additionalProperties": false
|
|
1180
1180
|
},
|
|
1181
|
-
"__@split@
|
|
1181
|
+
"__@split@6862": {
|
|
1182
1182
|
"type": "object",
|
|
1183
1183
|
"additionalProperties": false
|
|
1184
1184
|
},
|
|
1185
|
-
"__@matchAll@
|
|
1185
|
+
"__@matchAll@6864": {
|
|
1186
1186
|
"type": "object",
|
|
1187
1187
|
"additionalProperties": false
|
|
1188
1188
|
}
|
|
@@ -1844,23 +1844,23 @@
|
|
|
1844
1844
|
"dotAll": {
|
|
1845
1845
|
"type": "boolean"
|
|
1846
1846
|
},
|
|
1847
|
-
"__@match@
|
|
1847
|
+
"__@match@6855": {
|
|
1848
1848
|
"type": "object",
|
|
1849
1849
|
"additionalProperties": false
|
|
1850
1850
|
},
|
|
1851
|
-
"__@replace@
|
|
1851
|
+
"__@replace@6857": {
|
|
1852
1852
|
"type": "object",
|
|
1853
1853
|
"additionalProperties": false
|
|
1854
1854
|
},
|
|
1855
|
-
"__@search@
|
|
1855
|
+
"__@search@6860": {
|
|
1856
1856
|
"type": "object",
|
|
1857
1857
|
"additionalProperties": false
|
|
1858
1858
|
},
|
|
1859
|
-
"__@split@
|
|
1859
|
+
"__@split@6862": {
|
|
1860
1860
|
"type": "object",
|
|
1861
1861
|
"additionalProperties": false
|
|
1862
1862
|
},
|
|
1863
|
-
"__@matchAll@
|
|
1863
|
+
"__@matchAll@6864": {
|
|
1864
1864
|
"type": "object",
|
|
1865
1865
|
"additionalProperties": false
|
|
1866
1866
|
}
|
|
@@ -2522,23 +2522,23 @@
|
|
|
2522
2522
|
"dotAll": {
|
|
2523
2523
|
"type": "boolean"
|
|
2524
2524
|
},
|
|
2525
|
-
"__@match@
|
|
2525
|
+
"__@match@6855": {
|
|
2526
2526
|
"type": "object",
|
|
2527
2527
|
"additionalProperties": false
|
|
2528
2528
|
},
|
|
2529
|
-
"__@replace@
|
|
2529
|
+
"__@replace@6857": {
|
|
2530
2530
|
"type": "object",
|
|
2531
2531
|
"additionalProperties": false
|
|
2532
2532
|
},
|
|
2533
|
-
"__@search@
|
|
2533
|
+
"__@search@6860": {
|
|
2534
2534
|
"type": "object",
|
|
2535
2535
|
"additionalProperties": false
|
|
2536
2536
|
},
|
|
2537
|
-
"__@split@
|
|
2537
|
+
"__@split@6862": {
|
|
2538
2538
|
"type": "object",
|
|
2539
2539
|
"additionalProperties": false
|
|
2540
2540
|
},
|
|
2541
|
-
"__@matchAll@
|
|
2541
|
+
"__@matchAll@6864": {
|
|
2542
2542
|
"type": "object",
|
|
2543
2543
|
"additionalProperties": false
|
|
2544
2544
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Provider } from '@tinkoff/dippy';
|
|
2
|
+
import { provide } from '@tinkoff/dippy';
|
|
3
|
+
import {
|
|
4
|
+
COMMAND_PARAMETERS_TOKEN,
|
|
5
|
+
CONFIG_MANAGER_TOKEN,
|
|
6
|
+
SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
7
|
+
} from '../../../di/tokens';
|
|
8
|
+
import { createSelfSignedCertificate } from '../utils/selfSignedCertificate/createSelfSignedCertificate';
|
|
9
|
+
|
|
10
|
+
export const selfSignedCertificateProvider: Provider[] = [
|
|
11
|
+
provide({
|
|
12
|
+
provide: SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
13
|
+
useFactory: ({ configManager, parameters }) => {
|
|
14
|
+
const { host } = configManager;
|
|
15
|
+
if (configManager.httpProtocol === 'https') {
|
|
16
|
+
return createSelfSignedCertificate({
|
|
17
|
+
host,
|
|
18
|
+
certificatePath: parameters?.httpsCert,
|
|
19
|
+
keyPath: parameters?.httpsKey,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
},
|
|
24
|
+
deps: {
|
|
25
|
+
configManager: CONFIG_MANAGER_TOKEN,
|
|
26
|
+
parameters: COMMAND_PARAMETERS_TOKEN,
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { certificateDirectoryPath } from './common';
|
|
4
|
+
|
|
5
|
+
export const certificatedHostsListFilePath = path.resolve(
|
|
6
|
+
certificateDirectoryPath,
|
|
7
|
+
'certificatedHostsList'
|
|
8
|
+
);
|
|
9
|
+
const separator = ',';
|
|
10
|
+
|
|
11
|
+
export const createCertificatedHostsListFile = (data: string[]) => {
|
|
12
|
+
fs.writeFileSync(certificatedHostsListFilePath, data.join(separator), 'utf-8');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const readCertificatedHostsListFile = (): Set<string> | null => {
|
|
16
|
+
if (!fs.existsSync(certificatedHostsListFilePath)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const data = fs.readFileSync(certificatedHostsListFilePath, 'utf-8');
|
|
21
|
+
return new Set(data.split(separator));
|
|
22
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import child_process from 'child_process';
|
|
5
|
+
|
|
6
|
+
import { createSelfSignedCertificate } from './createSelfSignedCertificate';
|
|
7
|
+
import { certificatedHostsListFilePath } from './certificatedHostsList';
|
|
8
|
+
import { certificateDirectoryPath } from './common';
|
|
9
|
+
|
|
10
|
+
const defaultKeyPath = path.resolve(certificateDirectoryPath, 'localhost-key.pem');
|
|
11
|
+
const defaultCertificatePath = path.resolve(certificateDirectoryPath, 'localhost.pem');
|
|
12
|
+
const gitignorePath = path.resolve(process.cwd(), '.gitignore');
|
|
13
|
+
const customHost = 'localhost.domain.com';
|
|
14
|
+
|
|
15
|
+
jest.mock('os', () => {
|
|
16
|
+
return {
|
|
17
|
+
networkInterfaces: (param: string) => {
|
|
18
|
+
return [];
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const existsSyncSpy = jest.spyOn(fs, 'existsSync');
|
|
24
|
+
const mkdirSyncSpy = jest.spyOn(fs, 'mkdirSync');
|
|
25
|
+
const writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync');
|
|
26
|
+
const readFileSyncSpy = jest.spyOn(fs, 'readFileSync');
|
|
27
|
+
const execSyncSpy = jest.spyOn(child_process, 'execSync');
|
|
28
|
+
const appendFileSyncSpy = jest.spyOn(fs, 'appendFileSync');
|
|
29
|
+
|
|
30
|
+
describe('shared/utils/selfSignedCertificate', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
writeFileSyncSpy.mockImplementation(() => undefined);
|
|
33
|
+
mkdirSyncSpy.mockImplementation(() => {
|
|
34
|
+
return undefined;
|
|
35
|
+
});
|
|
36
|
+
execSyncSpy.mockImplementation((command) => {
|
|
37
|
+
if (command === 'mkcert -CAROOT') return '/location';
|
|
38
|
+
return '';
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
readFileSyncSpy.mockImplementation((path) => {
|
|
42
|
+
if (path === gitignorePath) return '';
|
|
43
|
+
return `localhost,${customHost}`;
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
jest.clearAllMocks();
|
|
48
|
+
existsSyncSpy.mockReset();
|
|
49
|
+
mkdirSyncSpy.mockReset();
|
|
50
|
+
writeFileSyncSpy.mockReset();
|
|
51
|
+
execSyncSpy.mockReset();
|
|
52
|
+
});
|
|
53
|
+
it('should generate certificate', async () => {
|
|
54
|
+
existsSyncSpy.mockImplementation((path) => {
|
|
55
|
+
if (path === certificatedHostsListFilePath || path === gitignorePath) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const result = createSelfSignedCertificate({});
|
|
62
|
+
|
|
63
|
+
expect(execSyncSpy).toHaveBeenNthCalledWith(
|
|
64
|
+
1,
|
|
65
|
+
`mkcert -install -key-file "${defaultKeyPath}" -cert-file "${defaultCertificatePath}" localhost`
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(result).toEqual({
|
|
69
|
+
keyPath: defaultKeyPath,
|
|
70
|
+
certificatePath: defaultCertificatePath,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it('should generate certificate for provided host', async () => {
|
|
74
|
+
existsSyncSpy.mockImplementation((path) => {
|
|
75
|
+
if (path === certificatedHostsListFilePath || path === gitignorePath) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
const result = createSelfSignedCertificate({ host: customHost });
|
|
81
|
+
expect(execSyncSpy).toHaveBeenNthCalledWith(
|
|
82
|
+
1,
|
|
83
|
+
`mkcert -install -key-file "${defaultKeyPath}" -cert-file "${defaultCertificatePath}" localhost ${customHost}`
|
|
84
|
+
);
|
|
85
|
+
expect(result).toEqual({
|
|
86
|
+
keyPath: defaultKeyPath,
|
|
87
|
+
certificatePath: defaultCertificatePath,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should return already existed certificate if it was generated previously', async () => {
|
|
92
|
+
existsSyncSpy.mockImplementation((path) => {
|
|
93
|
+
if (path === gitignorePath) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
const result = createSelfSignedCertificate({ host: customHost });
|
|
99
|
+
expect(execSyncSpy).toHaveBeenCalledTimes(0);
|
|
100
|
+
|
|
101
|
+
expect(result).toEqual({
|
|
102
|
+
keyPath: defaultKeyPath,
|
|
103
|
+
certificatePath: defaultCertificatePath,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should use custom certificate if provided', () => {
|
|
108
|
+
const customCertificate = {
|
|
109
|
+
certificatePath: path.resolve('path-to-custom-certificate/cert.pem'),
|
|
110
|
+
keyPath: path.resolve('path-to-custom-certificate/key.pem'),
|
|
111
|
+
};
|
|
112
|
+
const result = createSelfSignedCertificate({
|
|
113
|
+
host: customHost,
|
|
114
|
+
...customCertificate,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(execSyncSpy).toHaveBeenCalledTimes(0);
|
|
118
|
+
|
|
119
|
+
expect(result).toEqual({
|
|
120
|
+
keyPath: customCertificate.keyPath,
|
|
121
|
+
certificatePath: customCertificate.certificatePath,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it('if a new host be provided, a new certificate should be generated even if a certificate already exists.', () => {
|
|
125
|
+
existsSyncSpy.mockImplementation((path) => {
|
|
126
|
+
if (path === gitignorePath) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
});
|
|
131
|
+
const result = createSelfSignedCertificate({ host: 'new.localhost.domain' });
|
|
132
|
+
expect(execSyncSpy).toHaveBeenNthCalledWith(
|
|
133
|
+
1,
|
|
134
|
+
`mkcert -install -key-file "${defaultKeyPath}" -cert-file "${defaultCertificatePath}" localhost new.localhost.domain`
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(result).toEqual({
|
|
138
|
+
keyPath: defaultKeyPath,
|
|
139
|
+
certificatePath: defaultCertificatePath,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
it('should add certificates folder to .gitignore if .gitignore file exist', () => {
|
|
143
|
+
existsSyncSpy.mockImplementation((path) => {
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
const result = createSelfSignedCertificate({ host: 'new.localhost.domain' });
|
|
147
|
+
expect(appendFileSyncSpy).toBeCalledTimes(1);
|
|
148
|
+
|
|
149
|
+
expect(result).toEqual({
|
|
150
|
+
keyPath: defaultKeyPath,
|
|
151
|
+
certificatePath: defaultCertificatePath,
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import type { Certificate } from '../../types/base';
|
|
6
|
+
import { getHosts } from './getHosts';
|
|
7
|
+
import { certificateDirectoryPath, certificateDirectory } from './common';
|
|
8
|
+
import {
|
|
9
|
+
createCertificatedHostsListFile,
|
|
10
|
+
readCertificatedHostsListFile,
|
|
11
|
+
} from './certificatedHostsList';
|
|
12
|
+
|
|
13
|
+
interface CreateSelfSignedCertificateOptions {
|
|
14
|
+
host?: string;
|
|
15
|
+
keyPath?: string;
|
|
16
|
+
certificatePath?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line max-statements
|
|
20
|
+
export const createSelfSignedCertificate = (
|
|
21
|
+
options: CreateSelfSignedCertificateOptions
|
|
22
|
+
): Certificate => {
|
|
23
|
+
try {
|
|
24
|
+
const { host } = options;
|
|
25
|
+
const hosts = getHosts(host === '0.0.0.0' ? null : host);
|
|
26
|
+
const hostsFile = readCertificatedHostsListFile();
|
|
27
|
+
|
|
28
|
+
const defaultKeyPath = path.resolve(certificateDirectoryPath, 'localhost-key.pem');
|
|
29
|
+
const defaultCertificatePath = path.resolve(certificateDirectoryPath, 'localhost.pem');
|
|
30
|
+
|
|
31
|
+
const keyPath = options.keyPath ? path.resolve(options.keyPath) : defaultKeyPath;
|
|
32
|
+
const certificatePath = options.certificatePath
|
|
33
|
+
? path.resolve(options.certificatePath)
|
|
34
|
+
: defaultCertificatePath;
|
|
35
|
+
|
|
36
|
+
// check if certificates are already generated, in this case we simply skip step
|
|
37
|
+
|
|
38
|
+
const isExternalCertificateProvided = options.certificatePath && options.keyPath;
|
|
39
|
+
|
|
40
|
+
const isCertificateAlreadyExist =
|
|
41
|
+
!isExternalCertificateProvided &&
|
|
42
|
+
fs.existsSync(keyPath) &&
|
|
43
|
+
fs.existsSync(certificatePath) &&
|
|
44
|
+
hostsFile &&
|
|
45
|
+
hosts.every((h) => hostsFile.has(h));
|
|
46
|
+
|
|
47
|
+
const gitignorePath = path.resolve(process.cwd(), '.gitignore');
|
|
48
|
+
if (
|
|
49
|
+
fs.existsSync(gitignorePath) &&
|
|
50
|
+
!fs.readFileSync(gitignorePath, 'utf-8').includes(certificateDirectory)
|
|
51
|
+
) {
|
|
52
|
+
fs.appendFileSync(gitignorePath, `\n${certificateDirectory}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isExternalCertificateProvided || isCertificateAlreadyExist) {
|
|
56
|
+
console.log(
|
|
57
|
+
chalk.blue(
|
|
58
|
+
'Certificates for https environment are already exist, skipping step with certificate generation'
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
keyPath,
|
|
64
|
+
certificatePath,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fs.mkdirSync(certificateDirectoryPath, { recursive: true });
|
|
69
|
+
createCertificatedHostsListFile(hosts);
|
|
70
|
+
|
|
71
|
+
// TODO: maybe it is better to install mkcert for users for better dx
|
|
72
|
+
// check https://github.com/liuweiGL/vite-plugin-mkcert/blob/main/plugin/index.ts and https://github.com/vercel/next.js/blob/28fdc367b1f6d41245ea036bc40d1035a8a4d0e4/packages/next/src/lib/mkcert.ts#L68
|
|
73
|
+
execSync(
|
|
74
|
+
`mkcert -install -key-file "${keyPath}" -cert-file "${certificatePath}" ${hosts.join(' ')}`
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(keyPath) || !fs.existsSync(certificatePath)) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
'Error occured while certificate creation. Generated certificate files not found'
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const CAROOTFileLocation = execSync('mkcert -CAROOT').toString().trim();
|
|
84
|
+
|
|
85
|
+
console.log(chalk.green(`Root certificate located in ${chalk.blue(CAROOTFileLocation)}`));
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
keyPath,
|
|
89
|
+
certificatePath,
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(
|
|
93
|
+
chalk.red(
|
|
94
|
+
`Error while generating the certificate. The mkcert tool may not be installed please check and install it if that is the case.`
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
throw new Error(error);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
|
|
3
|
+
export const getDefaultHosts = () => {
|
|
4
|
+
const defaultHosts = ['localhost'];
|
|
5
|
+
const networkInterfaces = os.networkInterfaces();
|
|
6
|
+
|
|
7
|
+
for (const networkInterfaceKey in networkInterfaces) {
|
|
8
|
+
const networkInterfacesInfo = networkInterfaces[networkInterfaceKey];
|
|
9
|
+
|
|
10
|
+
if (networkInterfacesInfo) {
|
|
11
|
+
for (const info of networkInterfacesInfo) {
|
|
12
|
+
// https://github.com/nodejs/node/issues/42787
|
|
13
|
+
//@ts-expect-error
|
|
14
|
+
if (info.family === 'IPv4' || info.family === '4') {
|
|
15
|
+
defaultHosts.push(info.address);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return defaultHosts;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const getHosts = (host?: string) => {
|
|
25
|
+
const hosts = new Set([...getDefaultHosts()]);
|
|
26
|
+
|
|
27
|
+
if (host && !hosts.has(host)) {
|
|
28
|
+
hosts.add(host);
|
|
29
|
+
}
|
|
30
|
+
return Array.from(hosts);
|
|
31
|
+
};
|
package/src/api/start/index.ts
CHANGED
|
@@ -11,6 +11,9 @@ import type { Builder } from '../../typings/build/Builder';
|
|
|
11
11
|
|
|
12
12
|
export type Params = WithConfig<{
|
|
13
13
|
buildType?: 'server' | 'client' | 'all';
|
|
14
|
+
https?: boolean;
|
|
15
|
+
httpsKey?: string;
|
|
16
|
+
httpsCert?: string;
|
|
14
17
|
host?: string;
|
|
15
18
|
port?: number;
|
|
16
19
|
staticPort?: number;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Provider } from '@tinkoff/dippy';
|
|
2
2
|
import { provide } from '@tinkoff/dippy';
|
|
3
|
-
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { createProxyServer } from 'http-proxy';
|
|
4
5
|
import { INIT_HANDLER_TOKEN, CLOSE_HANDLER_TOKEN } from '../../tokens';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CONFIG_MANAGER_TOKEN,
|
|
8
|
+
SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
9
|
+
SERVER_TOKEN,
|
|
10
|
+
} from '../../../../di/tokens';
|
|
6
11
|
import { stopServer } from '../../utils/stopServer';
|
|
7
12
|
import { createServer } from '../../utils/createServer';
|
|
8
13
|
import { listenServer } from '../../utils/listenServer';
|
|
@@ -10,14 +15,24 @@ import { listenServer } from '../../utils/listenServer';
|
|
|
10
15
|
export const serverProviders: readonly Provider[] = [
|
|
11
16
|
provide({
|
|
12
17
|
provide: SERVER_TOKEN,
|
|
13
|
-
useFactory:
|
|
18
|
+
useFactory: ({ selfSignedCertificate }) => {
|
|
19
|
+
return createServer(selfSignedCertificate);
|
|
20
|
+
},
|
|
21
|
+
deps: {
|
|
22
|
+
selfSignedCertificate: {
|
|
23
|
+
token: SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
24
|
+
optional: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
14
27
|
}),
|
|
15
28
|
provide({
|
|
16
29
|
provide: INIT_HANDLER_TOKEN,
|
|
17
30
|
multi: true,
|
|
18
|
-
useFactory: ({ server, configManager }) => {
|
|
31
|
+
useFactory: ({ server, configManager, selfSignedCertificate }) => {
|
|
19
32
|
return async function staticServerListen() {
|
|
20
|
-
const {
|
|
33
|
+
const { https } = configManager;
|
|
34
|
+
const port = https && configManager.host !== '0.0.0.0' ? 443 : configManager.port;
|
|
35
|
+
const host = https ? '0.0.0.0' : configManager.host;
|
|
21
36
|
|
|
22
37
|
await listenServer(server, host, port);
|
|
23
38
|
};
|
|
@@ -25,6 +40,7 @@ export const serverProviders: readonly Provider[] = [
|
|
|
25
40
|
deps: {
|
|
26
41
|
server: SERVER_TOKEN,
|
|
27
42
|
configManager: CONFIG_MANAGER_TOKEN,
|
|
43
|
+
selfSignedCertificate: SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
28
44
|
},
|
|
29
45
|
}),
|
|
30
46
|
provide({
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Provider } from '@tinkoff/dippy';
|
|
2
2
|
import { provide } from '@tinkoff/dippy';
|
|
3
|
-
|
|
4
3
|
import { CLOSE_HANDLER_TOKEN, INIT_HANDLER_TOKEN } from '../../tokens';
|
|
5
4
|
import {
|
|
6
5
|
CONFIG_MANAGER_TOKEN,
|
|
@@ -8,14 +7,17 @@ import {
|
|
|
8
7
|
COMMAND_PARAMETERS_TOKEN,
|
|
9
8
|
STATIC_SERVER_TOKEN,
|
|
10
9
|
PORT_MANAGER_TOKEN,
|
|
10
|
+
SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
11
11
|
} from '../../../../di/tokens';
|
|
12
12
|
import { stopServer } from '../../utils/stopServer';
|
|
13
13
|
import type { ApplicationConfigEntry } from '../../../../typings/configEntry/application';
|
|
14
14
|
import { createConfigManager } from '../../../../config/configManager';
|
|
15
15
|
import { createServer } from '../../utils/createServer';
|
|
16
16
|
import { listenServer } from '../../utils/listenServer';
|
|
17
|
+
import { selfSignedCertificateProvider } from '../../../shared/providers/selfSignedCertificateProvider';
|
|
17
18
|
|
|
18
19
|
export const sharedProviders: readonly Provider[] = [
|
|
20
|
+
...selfSignedCertificateProvider,
|
|
19
21
|
provide({
|
|
20
22
|
provide: CONFIG_MANAGER_TOKEN,
|
|
21
23
|
useFactory: ({ configEntry, parameters, portManager }) =>
|
|
@@ -34,7 +36,15 @@ export const sharedProviders: readonly Provider[] = [
|
|
|
34
36
|
}),
|
|
35
37
|
provide({
|
|
36
38
|
provide: STATIC_SERVER_TOKEN,
|
|
37
|
-
useFactory:
|
|
39
|
+
useFactory: ({ selfSignedCertificate }) => {
|
|
40
|
+
return createServer(selfSignedCertificate);
|
|
41
|
+
},
|
|
42
|
+
deps: {
|
|
43
|
+
selfSignedCertificate: {
|
|
44
|
+
token: SELF_SIGNED_CERTIFICATE_TOKEN,
|
|
45
|
+
optional: true,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
38
48
|
}),
|
|
39
49
|
provide({
|
|
40
50
|
provide: INIT_HANDLER_TOKEN,
|
|
@@ -8,6 +8,7 @@ import { isApplication } from '../../../config/validate';
|
|
|
8
8
|
const label = (name) => chalk.bold.cyan(`▸ ${name}:`);
|
|
9
9
|
const link = (url) => chalk.underline.blue(url);
|
|
10
10
|
|
|
11
|
+
// eslint-disable-next-line max-statements
|
|
11
12
|
export function showBanner(di: Container) {
|
|
12
13
|
if (!di.get({ token: UI_SHOW_BANNER_TOKEN, optional: true })) {
|
|
13
14
|
return;
|
|
@@ -31,14 +32,21 @@ export function showBanner(di: Container) {
|
|
|
31
32
|
titleLines.push(`${label('FileSystemPages')} true`);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
// TODO: add information about hosted url !
|
|
36
|
+
const baseServerUrl = `${config.httpProtocol}://${config.host.replace('0.0.0.0', 'localhost')}`;
|
|
37
|
+
const server = `${baseServerUrl}:${config.port}`;
|
|
38
|
+
|
|
39
|
+
const staticServer = `${config.httpProtocol}://${config.staticHost.replace(
|
|
40
|
+
'0.0.0.0',
|
|
41
|
+
'localhost'
|
|
42
|
+
)}:${config.staticPort}`;
|
|
38
43
|
|
|
39
44
|
if (config.type === 'application') {
|
|
40
45
|
// Listeners
|
|
41
46
|
messageLines.push(chalk.bold('Static: ') + link(staticServer));
|
|
47
|
+
if (config.host !== '0.0.0.0' && config.https) {
|
|
48
|
+
messageLines.push(chalk.bold('App: ') + link(baseServerUrl));
|
|
49
|
+
}
|
|
42
50
|
messageLines.push(chalk.bold('App: ') + link(server));
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { createServer as httpCreateServer } from 'http';
|
|
2
|
+
import { createServer as httpsCreateServer } from 'https';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
2
4
|
import stoppable from 'stoppable';
|
|
5
|
+
import type { Certificate } from '../../shared/types/base';
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
const createHttpsServer = ({ keyPath, certificatePath }: Certificate) => {
|
|
8
|
+
const options = {
|
|
9
|
+
key: readFileSync(keyPath),
|
|
10
|
+
cert: readFileSync(certificatePath),
|
|
11
|
+
};
|
|
12
|
+
const httpsServer = httpsCreateServer(options);
|
|
13
|
+
return stoppable(httpsServer, 0);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const createHttpServer = () => {
|
|
5
17
|
const server = httpCreateServer();
|
|
6
18
|
|
|
7
19
|
return stoppable(server, 0);
|
|
8
20
|
};
|
|
21
|
+
|
|
22
|
+
export const createServer = (certificate?: Certificate) => {
|
|
23
|
+
return certificate && certificate?.certificatePath && certificate?.keyPath
|
|
24
|
+
? createHttpsServer(certificate)
|
|
25
|
+
: createHttpServer();
|
|
26
|
+
};
|