1 | #!/usr/bin/env node
|
2 |
|
3 |
|
4 | const path = require('path');
|
5 | const {existsSync} = require('fs');
|
6 |
|
7 |
|
8 | const arg = require('arg');
|
9 |
|
10 |
|
11 | const serve = require('../lib');
|
12 | const handle = require('../lib/handler');
|
13 | const {version} = require('../package');
|
14 | const logError = require('../lib/error');
|
15 | const parseEndpoint = require('../lib/parse-endpoint.js');
|
16 |
|
17 |
|
18 | const args = arg({
|
19 | '--listen': [parseEndpoint],
|
20 | '-l': '--listen',
|
21 |
|
22 | '--help': Boolean,
|
23 |
|
24 | '--version': Boolean,
|
25 | '-v': '--version',
|
26 |
|
27 |
|
28 | '--port': Number,
|
29 | '-p': '--port',
|
30 | '--host': String,
|
31 | '-h': '--host',
|
32 | '--unix-socket': String,
|
33 | '-s': '--unix-socket'
|
34 | });
|
35 |
|
36 |
|
37 |
|
38 | if (args['--help']) {
|
39 | console.error(`
|
40 | micro - Asynchronous HTTP microservices
|
41 |
|
42 | USAGE
|
43 |
|
44 | $ micro --help
|
45 | $ micro --version
|
46 | $ micro [-l listen_uri [-l ...]] [entry_point.js]
|
47 |
|
48 | By default micro will listen on 0.0.0.0:3000 and will look first
|
49 | for the "main" property in package.json and subsequently for index.js
|
50 | as the default entry_point.
|
51 |
|
52 | Specifying a single --listen argument will overwrite the default, not supplement it.
|
53 |
|
54 | OPTIONS
|
55 |
|
56 | --help shows this help message
|
57 |
|
58 | -v, --version displays the current version of micro
|
59 |
|
60 | -l, --listen listen_uri specify a URI endpoint on which to listen (see below) -
|
61 | more than one may be specified to listen in multiple places
|
62 |
|
63 | ENDPOINTS
|
64 |
|
65 | Listen endpoints (specified by the --listen or -l options above) instruct micro
|
66 | to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes.
|
67 |
|
68 | For TCP (traditional host/port) endpoints:
|
69 |
|
70 | $ micro -l tcp://hostname:1234
|
71 |
|
72 | For UNIX domain socket endpoints:
|
73 |
|
74 | $ micro -l unix:/path/to/socket.sock
|
75 |
|
76 | For Windows named pipe endpoints:
|
77 |
|
78 | $ micro -l pipe:\\\\.\\pipe\\PipeName
|
79 | `);
|
80 | process.exit(2);
|
81 | }
|
82 |
|
83 |
|
84 |
|
85 | if (args['--version']) {
|
86 | console.log(version);
|
87 | process.exit();
|
88 | }
|
89 |
|
90 | if ((args['--port'] || args['--host']) && args['--unix-socket']) {
|
91 | logError(
|
92 | `Both host/port and socket provided. You can only use one.`,
|
93 | 'invalid-port-socket'
|
94 | );
|
95 | process.exit(1);
|
96 | }
|
97 |
|
98 | let deprecatedEndpoint = null;
|
99 |
|
100 | args['--listen'] = args['--listen'] || [];
|
101 |
|
102 | if (args['--port']) {
|
103 | const {isNaN} = Number;
|
104 | const port = Number(args['--port']);
|
105 | if (isNaN(port) || (!isNaN(port) && (port < 1 || port >= Math.pow(2, 16)))) {
|
106 | logError(
|
107 | `Port option must be a number. Supplied: ${args['--port']}`,
|
108 | 'invalid-server-port'
|
109 | );
|
110 | process.exit(1);
|
111 | }
|
112 |
|
113 | deprecatedEndpoint = [args['--port']];
|
114 | }
|
115 |
|
116 | if (args['--host']) {
|
117 | deprecatedEndpoint = deprecatedEndpoint || [];
|
118 | deprecatedEndpoint.push(args['--host']);
|
119 | }
|
120 |
|
121 | if (deprecatedEndpoint) {
|
122 | args['--listen'].push(deprecatedEndpoint);
|
123 | }
|
124 |
|
125 | if (args['--unix-socket']) {
|
126 | if (typeof args['--unix-socket'] === 'boolean') {
|
127 | logError(
|
128 | `Socket must be a string. A boolean was provided.`,
|
129 | 'invalid-socket'
|
130 | );
|
131 | }
|
132 | args['--listen'].push(args['--unix-socket']);
|
133 | }
|
134 |
|
135 | if (args['--port'] || args['--host'] || args['--unix-socket']) {
|
136 | logError(
|
137 | '--port, --host, and --unix-socket are deprecated - see --help for information on the --listen flag',
|
138 | 'deprecated-endpoint-flags'
|
139 | );
|
140 | }
|
141 |
|
142 | if (args['--listen'].length === 0) {
|
143 |
|
144 | args['--listen'].push([3000]);
|
145 | }
|
146 |
|
147 | let file = args._[0];
|
148 |
|
149 | if (!file) {
|
150 | try {
|
151 | const packageJson = require(path.resolve(process.cwd(), 'package.json'));
|
152 | file = packageJson.main || 'index.js';
|
153 | } catch (err) {
|
154 | if (err.code !== 'MODULE_NOT_FOUND') {
|
155 | logError(
|
156 | `Could not read \`package.json\`: ${err.message}`,
|
157 | 'invalid-package-json'
|
158 | );
|
159 | process.exit(1);
|
160 | }
|
161 | }
|
162 | }
|
163 |
|
164 | if (!file) {
|
165 | logError('Please supply a file!', 'path-missing');
|
166 | process.exit(1);
|
167 | }
|
168 |
|
169 | if (file[0] !== '/') {
|
170 | file = path.resolve(process.cwd(), file);
|
171 | }
|
172 |
|
173 | if (!existsSync(file)) {
|
174 | logError(
|
175 | `The file or directory "${path.basename(file)}" doesn't exist!`,
|
176 | 'path-not-existent'
|
177 | );
|
178 | process.exit(1);
|
179 | }
|
180 |
|
181 | function registerShutdown(fn) {
|
182 | let run = false;
|
183 |
|
184 | const wrapper = () => {
|
185 | if (!run) {
|
186 | run = true;
|
187 | fn();
|
188 | }
|
189 | };
|
190 |
|
191 | process.on('SIGINT', wrapper);
|
192 | process.on('SIGTERM', wrapper);
|
193 | process.on('exit', wrapper);
|
194 | }
|
195 |
|
196 | function startEndpoint(module, endpoint) {
|
197 | const server = serve(module);
|
198 |
|
199 | server.on('error', err => {
|
200 | console.error('micro:', err.stack);
|
201 | process.exit(1);
|
202 | });
|
203 |
|
204 | server.listen(...endpoint, () => {
|
205 | const details = server.address();
|
206 |
|
207 | registerShutdown(() => server.close());
|
208 |
|
209 |
|
210 |
|
211 | if (typeof details === 'string') {
|
212 | console.log(`micro: Accepting connections on ${details}`);
|
213 | } else if (typeof details === 'object' && details.port) {
|
214 | console.log(`micro: Accepting connections on port ${details.port}`);
|
215 | } else {
|
216 | console.log('micro: Accepting connections');
|
217 | }
|
218 | });
|
219 | }
|
220 |
|
221 | async function start() {
|
222 | const loadedModule = await handle(file);
|
223 |
|
224 | for (const endpoint of args['--listen']) {
|
225 | startEndpoint(loadedModule, endpoint);
|
226 | }
|
227 |
|
228 | registerShutdown(() => console.log('micro: Gracefully shutting down. Please wait...'));
|
229 | }
|
230 |
|
231 | start();
|