rtjscomp 0.8.1 → 0.8.3
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/package.json +16 -9
- package/rtjscomp.js +241 -170
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rtjscomp",
|
|
3
|
-
"
|
|
4
|
-
"license": "Zlib",
|
|
5
|
-
"version": "0.8.1",
|
|
3
|
+
"version": "0.8.3",
|
|
6
4
|
"description": "php-like server but with javascript",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/L3P3/rtjscomp"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/L3P3/rtjscomp/issues"
|
|
11
|
+
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"ipware": "latest",
|
|
14
14
|
"parse-multipart-data": "latest",
|
|
@@ -19,5 +19,12 @@
|
|
|
19
19
|
},
|
|
20
20
|
"bin": {
|
|
21
21
|
"rtjscomp": "./rtjscomp.js"
|
|
22
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"http",
|
|
25
|
+
"javascript",
|
|
26
|
+
"server"
|
|
27
|
+
],
|
|
28
|
+
"author": "L3P3 <dev@l3p3.de> (https://l3p3.de)",
|
|
29
|
+
"license": "Zlib"
|
|
23
30
|
}
|
package/rtjscomp.js
CHANGED
|
@@ -25,7 +25,9 @@ const GZIP_OPTIONS = {level: 9};
|
|
|
25
25
|
const AGENT_CHECK_BOT = /bot|googlebot|crawler|spider|robot|crawling|favicon/i;
|
|
26
26
|
const AGENT_CHECK_MOBIL = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
|
|
27
27
|
const HTTP_LIST_REG = /,\s*/;
|
|
28
|
+
const IMPORT_REG = /import\(/g;
|
|
28
29
|
|
|
30
|
+
let log_verbose = process.argv.includes('-v');
|
|
29
31
|
let port_http = 0;
|
|
30
32
|
let port_https = 0;
|
|
31
33
|
const file_type_mimes = new Map;
|
|
@@ -61,7 +63,7 @@ if (!Object.fromEntries) {
|
|
|
61
63
|
global.globals = rtjscomp;
|
|
62
64
|
global.actions = rtjscomp.actions;
|
|
63
65
|
global.data_load = name => {
|
|
64
|
-
log('[deprecated
|
|
66
|
+
log('[deprecated] synchronous load: ' + PATH_DATA + name);
|
|
65
67
|
try {
|
|
66
68
|
return fs.readFileSync(PATH_DATA + name, 'utf8');
|
|
67
69
|
}
|
|
@@ -70,7 +72,7 @@ global.data_load = name => {
|
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
global.data_save = (name, data) => (
|
|
73
|
-
log('[deprecated
|
|
75
|
+
log('[deprecated] synchronous save: ' + PATH_DATA + name),
|
|
74
76
|
fs.writeFileSync(PATH_DATA + name, data, 'utf8')
|
|
75
77
|
)
|
|
76
78
|
global.number_check_int = number => (
|
|
@@ -81,13 +83,12 @@ global.number_check_uint = number => (
|
|
|
81
83
|
)
|
|
82
84
|
|
|
83
85
|
rtjscomp.data_load = async name => {
|
|
84
|
-
log('load: ' + name);
|
|
86
|
+
if (log_verbose) log('load: ' + PATH_DATA + name);
|
|
85
87
|
const data = await fsp.readFile(PATH_DATA + name, 'utf8').catch(() => null);
|
|
86
88
|
return name.endsWith('.json') ? JSON.parse(data || null) : data;
|
|
87
89
|
}
|
|
88
90
|
rtjscomp.data_load_watch = (name, callback) => (
|
|
89
91
|
file_keep_new(PATH_DATA + name, data => (
|
|
90
|
-
log('load: ' + name),
|
|
91
92
|
callback(
|
|
92
93
|
name.endsWith('.json')
|
|
93
94
|
? JSON.parse(data || null)
|
|
@@ -96,7 +97,7 @@ rtjscomp.data_load_watch = (name, callback) => (
|
|
|
96
97
|
))
|
|
97
98
|
)
|
|
98
99
|
rtjscomp.data_save = (name, data) => (
|
|
99
|
-
log('save: ' + name),
|
|
100
|
+
log_verbose && log('save: ' + PATH_DATA + name),
|
|
100
101
|
fsp.writeFile(
|
|
101
102
|
PATH_DATA + name,
|
|
102
103
|
name.endsWith('.json') ? JSON.stringify(data) : data,
|
|
@@ -104,83 +105,143 @@ rtjscomp.data_save = (name, data) => (
|
|
|
104
105
|
)
|
|
105
106
|
)
|
|
106
107
|
|
|
108
|
+
const custom_require_paths = new Set;
|
|
107
109
|
const custom_require_cache = new Map;
|
|
110
|
+
const custom_import_cache = new Map;
|
|
108
111
|
const custom_require = path => {
|
|
109
112
|
let result = custom_require_cache.get(path);
|
|
110
113
|
if (result != null) return result;
|
|
111
114
|
|
|
115
|
+
log_verbose && log('require: ' + path);
|
|
116
|
+
const path_real = require.resolve(path, resolve_options);
|
|
117
|
+
custom_require_paths.add(path_real);
|
|
112
118
|
custom_require_cache.set(
|
|
113
119
|
path,
|
|
114
|
-
result = require(
|
|
115
|
-
|
|
120
|
+
result = require(path_real)
|
|
121
|
+
);
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
const custom_import = async path => {
|
|
125
|
+
let result = custom_import_cache.get(path);
|
|
126
|
+
if (result != null) return result;
|
|
127
|
+
|
|
128
|
+
log_verbose && log('import: ' + path);
|
|
129
|
+
custom_import_cache.set(
|
|
130
|
+
path,
|
|
131
|
+
result = await import(
|
|
132
|
+
'file://' + require.resolve(path, resolve_options)
|
|
116
133
|
)
|
|
117
134
|
);
|
|
118
135
|
return result;
|
|
119
136
|
}
|
|
137
|
+
actions.module_cache_clear = () => {
|
|
138
|
+
for (const path of custom_require_paths) {
|
|
139
|
+
delete require.cache[path];
|
|
140
|
+
}
|
|
141
|
+
custom_require_cache.clear();
|
|
142
|
+
custom_import_cache.clear();
|
|
143
|
+
}
|
|
120
144
|
|
|
121
145
|
const services_active = new Map;
|
|
146
|
+
const services_loading = new Set;
|
|
122
147
|
const services_list_react = async () => {
|
|
123
148
|
await Promise.all(
|
|
124
149
|
Array.from(services_active.entries())
|
|
125
150
|
.filter(([path, _]) => !services.has(path))
|
|
126
151
|
.map(([_, service_object]) => service_stop(service_object, true))
|
|
127
152
|
);
|
|
128
|
-
for (const path of services)
|
|
129
|
-
|
|
153
|
+
for (const path of services)
|
|
154
|
+
if (!services_active.has(path)) {
|
|
155
|
+
await service_start(path);
|
|
130
156
|
}
|
|
131
157
|
}
|
|
132
158
|
const service_start = async path => {
|
|
133
159
|
const service_object = {
|
|
160
|
+
content: null,
|
|
161
|
+
handler_stop: null,
|
|
134
162
|
path,
|
|
135
|
-
start: null,
|
|
136
|
-
started: false,
|
|
137
|
-
stop: null,
|
|
138
163
|
stopped: false,
|
|
139
164
|
};
|
|
140
165
|
|
|
141
166
|
await file_keep_new(PATH_PUBLIC + path + '.service.js', async file_content => {
|
|
142
167
|
if (file_content === null) {
|
|
143
|
-
log('error
|
|
144
|
-
|
|
145
|
-
return;
|
|
168
|
+
log('[error] service file not found: ' + path);
|
|
169
|
+
return service_stop(service_object, true);
|
|
146
170
|
}
|
|
147
|
-
|
|
148
|
-
|
|
171
|
+
if (services_loading.size > 0) {
|
|
172
|
+
await Promise.all(Array.from(services_loading));
|
|
173
|
+
}
|
|
174
|
+
const start_promise = service_start_inner(path, service_object, file_content);
|
|
175
|
+
services_loading.add(start_promise);
|
|
176
|
+
await start_promise;
|
|
177
|
+
services_loading.delete(start_promise);
|
|
149
178
|
});
|
|
150
179
|
}
|
|
151
180
|
const service_start_inner = async (path, service_object, file_content) => {
|
|
181
|
+
if (services_active.has(path)) {
|
|
182
|
+
await service_stop_handler(service_object);
|
|
183
|
+
}
|
|
184
|
+
const content_object = service_object.content = {};
|
|
185
|
+
log('start service: ' + path);
|
|
186
|
+
|
|
187
|
+
const start_interval = setInterval(() => {
|
|
188
|
+
log(`[warning] ${path}: still starting`);
|
|
189
|
+
}, 1e3);
|
|
190
|
+
|
|
191
|
+
if (file_content.includes('globals.')) {
|
|
192
|
+
log(`[deprecated] ${path}: uses globals object`);
|
|
193
|
+
}
|
|
194
|
+
|
|
152
195
|
try {
|
|
153
|
-
const fun =
|
|
154
|
-
|
|
155
|
-
`const log=a=>rtjscomp.log(${
|
|
196
|
+
const fun = (0, eval)(
|
|
197
|
+
`(async function(require,custom_import){const log=a=>rtjscomp.log(${
|
|
156
198
|
JSON.stringify(path + ': ')
|
|
157
|
-
}+a);${
|
|
199
|
+
}+a);${
|
|
200
|
+
file_content.replace(IMPORT_REG, 'custom_import(') + '\n'
|
|
201
|
+
}})`
|
|
158
202
|
);
|
|
159
|
-
fun.call(
|
|
203
|
+
const result = await fun.call(content_object, custom_require, custom_import);
|
|
204
|
+
if (service_object.stopped) {
|
|
205
|
+
clearInterval(start_interval);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (typeof result === 'function') {
|
|
209
|
+
service_object.handler_stop = result;
|
|
210
|
+
}
|
|
160
211
|
}
|
|
161
212
|
catch (err) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return;
|
|
213
|
+
clearInterval(start_interval);
|
|
214
|
+
log(`[error] ${path}: ${err.message}`);
|
|
215
|
+
if (service_object.stopped) return;
|
|
216
|
+
return service_stop(service_object, false);
|
|
165
217
|
}
|
|
166
218
|
|
|
167
|
-
|
|
219
|
+
const handler_start = content_object.start;
|
|
220
|
+
if (handler_start) {
|
|
221
|
+
log(`[deprecated] ${path}: has start method`);
|
|
222
|
+
delete content_object.start;
|
|
168
223
|
try {
|
|
169
|
-
await
|
|
170
|
-
service_object.start = null;
|
|
171
|
-
service_object.started = true;
|
|
224
|
+
await handler_start();
|
|
172
225
|
}
|
|
173
226
|
catch (err) {
|
|
174
|
-
|
|
175
|
-
log(`error
|
|
176
|
-
return;
|
|
227
|
+
clearInterval(start_interval);
|
|
228
|
+
log(`[error] ${path} start: ${err.message}`);
|
|
229
|
+
return service_stop(service_object, false);
|
|
177
230
|
}
|
|
178
231
|
}
|
|
232
|
+
clearInterval(start_interval);
|
|
233
|
+
|
|
234
|
+
if (content_object.stop) {
|
|
235
|
+
log(`[deprecated] ${path}: has stop method`);
|
|
236
|
+
service_object.handler_stop = content_object.stop;
|
|
237
|
+
delete content_object.stop;
|
|
238
|
+
}
|
|
239
|
+
|
|
179
240
|
services_active.set(path, service_object);
|
|
180
|
-
log('service
|
|
241
|
+
if (log_verbose) log('started service: ' + path);
|
|
181
242
|
}
|
|
182
243
|
const services_shutdown = () => (
|
|
183
|
-
log('shutdown services...'),
|
|
244
|
+
log_verbose && log('shutdown services...'),
|
|
184
245
|
Promise.all(
|
|
185
246
|
Array.from(services_active.values())
|
|
186
247
|
.map(service_object => service_stop(service_object, true))
|
|
@@ -190,28 +251,39 @@ const service_stop = async (service_object, forget) => {
|
|
|
190
251
|
service_object.stopped = true;
|
|
191
252
|
if (forget) fs.unwatchFile(PATH_PUBLIC + service_object.path + '.service.js');
|
|
192
253
|
await service_stop_handler(service_object);
|
|
193
|
-
services_active.delete(service_object.path);
|
|
194
|
-
log('service stopped: ' + service_object.path);
|
|
195
254
|
}
|
|
196
255
|
const service_stop_handler = async service_object => {
|
|
197
|
-
|
|
256
|
+
services_active.delete(service_object.path);
|
|
257
|
+
log('stop service: ' + service_object.path);
|
|
258
|
+
const handler_stop = service_object.handler_stop;
|
|
259
|
+
if (handler_stop) {
|
|
260
|
+
const stop_interval = setInterval(() => {
|
|
261
|
+
log(`[warning] ${service_object.path}: still stopping`);
|
|
262
|
+
}, 1e3);
|
|
198
263
|
try {
|
|
199
|
-
|
|
200
|
-
|
|
264
|
+
service_object.handler_stop = null;
|
|
265
|
+
await handler_stop();
|
|
201
266
|
}
|
|
202
267
|
catch (err) {
|
|
203
|
-
log(`error
|
|
268
|
+
log(`[error] ${service_object.path} stop: ${err.message}`);
|
|
204
269
|
}
|
|
270
|
+
clearInterval(stop_interval);
|
|
205
271
|
}
|
|
272
|
+
if (log_verbose) log('stopped service: ' + service_object.path);
|
|
206
273
|
}
|
|
207
274
|
global.service_require = path => {
|
|
208
275
|
const service = services_active.get(path);
|
|
209
|
-
if (service) return service;
|
|
276
|
+
if (service != null) return service.content;
|
|
210
277
|
throw new Error('service required: ' + path);
|
|
211
278
|
}
|
|
212
|
-
global.service_require_try = path =>
|
|
213
|
-
services_active.get(path)
|
|
214
|
-
|
|
279
|
+
global.service_require_try = path => {
|
|
280
|
+
const service = services_active.get(path);
|
|
281
|
+
return (
|
|
282
|
+
service != null
|
|
283
|
+
? service.content
|
|
284
|
+
: null
|
|
285
|
+
);
|
|
286
|
+
}
|
|
215
287
|
|
|
216
288
|
const map_generate_bol = (set, data) => {
|
|
217
289
|
set.clear();
|
|
@@ -237,7 +309,7 @@ const map_generate_equ = (map, data) => {
|
|
|
237
309
|
|
|
238
310
|
const file_compare = (curr, prev, path) => (
|
|
239
311
|
curr.mtime > prev.mtime && (
|
|
240
|
-
log('file changed: ' + path),
|
|
312
|
+
log_verbose && log('file changed: ' + path),
|
|
241
313
|
true
|
|
242
314
|
)
|
|
243
315
|
)
|
|
@@ -251,7 +323,9 @@ const file_watch = (path, callback) => (
|
|
|
251
323
|
)
|
|
252
324
|
const file_keep_new = async (path, callback) => {
|
|
253
325
|
try {
|
|
254
|
-
|
|
326
|
+
const data = await fsp.readFile(path, 'utf8');
|
|
327
|
+
if (log_verbose) log('load file: ' + path);
|
|
328
|
+
await callback(data);
|
|
255
329
|
fs.watchFile(path, async (curr, prev) => {
|
|
256
330
|
if (file_compare(curr, prev, path)) {
|
|
257
331
|
await callback(
|
|
@@ -272,7 +346,7 @@ actions.log_clear = () => {
|
|
|
272
346
|
const log = rtjscomp.log = msg => (
|
|
273
347
|
console.log(msg),
|
|
274
348
|
log_history.push(msg),
|
|
275
|
-
spam('log', [msg])
|
|
349
|
+
spam_enabled ? spam('log', [msg]) : undefined
|
|
276
350
|
)
|
|
277
351
|
|
|
278
352
|
const spam_enabled = fs.existsSync('spam.csv');
|
|
@@ -281,17 +355,16 @@ actions.spam_save = async (muted = false) => {
|
|
|
281
355
|
if (!spam_enabled) return;
|
|
282
356
|
|
|
283
357
|
try {
|
|
284
|
-
|
|
358
|
+
const tmp = rtjscomp.spam_history;
|
|
285
359
|
rtjscomp.spam_history = '';
|
|
286
|
-
|
|
360
|
+
await fsp.appendFile('spam.csv', tmp, 'utf8');
|
|
361
|
+
if (log_verbose && !muted) log('spam.csv saved');
|
|
287
362
|
}
|
|
288
363
|
catch (err) {
|
|
289
|
-
log('error
|
|
364
|
+
log('[error] cannot save spam.csv: ' + err.message);
|
|
290
365
|
}
|
|
291
366
|
}
|
|
292
367
|
const spam = (type, data) => {
|
|
293
|
-
if (!spam_enabled) return;
|
|
294
|
-
|
|
295
368
|
rtjscomp.spam_history += (
|
|
296
369
|
Date.now() +
|
|
297
370
|
',' +
|
|
@@ -315,7 +388,7 @@ const request_handle = async (request, response, https) => {
|
|
|
315
388
|
https = request_headers['x-forwarded-proto'] === 'https';
|
|
316
389
|
}
|
|
317
390
|
|
|
318
|
-
spam('request', [https, request.url, request_ip]);
|
|
391
|
+
if (spam_enabled) spam('request', [https, request.url, request_ip]);
|
|
319
392
|
|
|
320
393
|
try {
|
|
321
394
|
const request_url_parsed = url.parse(request.url, false);
|
|
@@ -422,11 +495,11 @@ const request_handle = async (request, response, https) => {
|
|
|
422
495
|
file_function = file_cache_functions.get(path);
|
|
423
496
|
}
|
|
424
497
|
else {
|
|
425
|
-
log(`load ${
|
|
498
|
+
if (log_verbose) log(`load ${
|
|
426
499
|
file_dyn_enabled
|
|
427
500
|
? 'dynam'
|
|
428
501
|
: 'stat'
|
|
429
|
-
}ic file: ${
|
|
502
|
+
}ic file: ${path_real}`);
|
|
430
503
|
|
|
431
504
|
if (
|
|
432
505
|
file_privates.has(path) ||
|
|
@@ -442,9 +515,12 @@ const request_handle = async (request, response, https) => {
|
|
|
442
515
|
if (file_content.includes('\r')) {
|
|
443
516
|
throw 'illegal line break, must be unix';
|
|
444
517
|
}
|
|
518
|
+
if (file_content.includes('globals.')) {
|
|
519
|
+
log(`[deprecated] ${path}: uses globals object`);
|
|
520
|
+
}
|
|
445
521
|
const file_content_length = file_content.length;
|
|
446
522
|
|
|
447
|
-
let code = `async (input,output,request,response,require)=>{const log=a=>rtjscomp.log(${
|
|
523
|
+
let code = `async (input,output,request,response,require,custom_import)=>{const log=a=>rtjscomp.log(${
|
|
448
524
|
JSON.stringify(path + ': ')
|
|
449
525
|
}+a);`;
|
|
450
526
|
|
|
@@ -469,19 +545,23 @@ const request_handle = async (request, response, https) => {
|
|
|
469
545
|
// `<?`?
|
|
470
546
|
if (file_content.charCodeAt(index_start) !== 61) {
|
|
471
547
|
code += (
|
|
472
|
-
file_content
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
548
|
+
file_content
|
|
549
|
+
.substring(
|
|
550
|
+
index_start,
|
|
551
|
+
index_end
|
|
552
|
+
)
|
|
553
|
+
.replace(IMPORT_REG, 'custom_import(') +
|
|
476
554
|
';'
|
|
477
555
|
);
|
|
478
556
|
}
|
|
479
557
|
else { // `<?=`?
|
|
480
558
|
code += `output.write(''+(${
|
|
481
|
-
file_content
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
559
|
+
file_content
|
|
560
|
+
.substring(
|
|
561
|
+
++index_start,
|
|
562
|
+
index_end
|
|
563
|
+
)
|
|
564
|
+
.replace(IMPORT_REG, 'custom_import(')
|
|
485
565
|
}));`;
|
|
486
566
|
}
|
|
487
567
|
}
|
|
@@ -516,14 +596,14 @@ const request_handle = async (request, response, https) => {
|
|
|
516
596
|
}
|
|
517
597
|
|
|
518
598
|
try {
|
|
519
|
-
file_function = eval(code += '}');
|
|
599
|
+
file_function = (0, eval)(code += '}');
|
|
520
600
|
}
|
|
521
601
|
catch (err) {
|
|
522
602
|
throw err.message;
|
|
523
603
|
}
|
|
524
604
|
}
|
|
525
605
|
catch (err) {
|
|
526
|
-
log(
|
|
606
|
+
log(`[error] ${path} compile: ${err}`);
|
|
527
607
|
throw 500;
|
|
528
608
|
}
|
|
529
609
|
|
|
@@ -578,7 +658,7 @@ const request_handle = async (request, response, https) => {
|
|
|
578
658
|
);
|
|
579
659
|
}
|
|
580
660
|
catch (err) {
|
|
581
|
-
log(
|
|
661
|
+
log(`[error] ${path} request query: ${err.message}`);
|
|
582
662
|
throw 400;
|
|
583
663
|
}
|
|
584
664
|
}
|
|
@@ -613,7 +693,7 @@ const request_handle = async (request, response, https) => {
|
|
|
613
693
|
}
|
|
614
694
|
}
|
|
615
695
|
catch (err) {
|
|
616
|
-
log(
|
|
696
|
+
log(`[error] ${path} request body: ${err.message}`);
|
|
617
697
|
throw 400;
|
|
618
698
|
}
|
|
619
699
|
}
|
|
@@ -641,7 +721,7 @@ const request_handle = async (request, response, https) => {
|
|
|
641
721
|
file_function_output = response;
|
|
642
722
|
}
|
|
643
723
|
|
|
644
|
-
spam('execute', [
|
|
724
|
+
if (spam_enabled) spam('execute', [
|
|
645
725
|
path,
|
|
646
726
|
Object.fromEntries(
|
|
647
727
|
Object.entries(file_function_input)
|
|
@@ -653,19 +733,24 @@ const request_handle = async (request, response, https) => {
|
|
|
653
733
|
)
|
|
654
734
|
]);
|
|
655
735
|
|
|
736
|
+
if (services_loading.size > 0) {
|
|
737
|
+
await Promise.all(Array.from(services_loading));
|
|
738
|
+
}
|
|
739
|
+
|
|
656
740
|
try {
|
|
657
741
|
await file_function(
|
|
658
742
|
file_function_input,
|
|
659
743
|
file_function_output,
|
|
660
744
|
request,
|
|
661
745
|
response,
|
|
662
|
-
custom_require
|
|
746
|
+
custom_require,
|
|
747
|
+
custom_import
|
|
663
748
|
);
|
|
664
749
|
file_function_output.end();
|
|
665
750
|
}
|
|
666
751
|
catch (err) {
|
|
667
752
|
if (err instanceof Error) {
|
|
668
|
-
log(`error
|
|
753
|
+
log(`[error] ${path}: ${err.message}`);
|
|
669
754
|
|
|
670
755
|
if (err.message.startsWith('service required: ')) {
|
|
671
756
|
err = 503;
|
|
@@ -698,7 +783,7 @@ const request_handle = async (request, response, https) => {
|
|
|
698
783
|
file_data = fs.createReadStream(path_real);
|
|
699
784
|
}
|
|
700
785
|
|
|
701
|
-
spam('static_send', [path, file_gz_enabled]);
|
|
786
|
+
if (spam_enabled) spam('static_send', [path, file_gz_enabled]);
|
|
702
787
|
response.setHeader('Cache-Control', 'public, max-age=600');
|
|
703
788
|
|
|
704
789
|
if (file_gz_enabled) {
|
|
@@ -718,8 +803,8 @@ const request_handle = async (request, response, https) => {
|
|
|
718
803
|
err = 500;
|
|
719
804
|
}
|
|
720
805
|
|
|
721
|
-
if (err >= 400) {
|
|
722
|
-
log(`error ${err}
|
|
806
|
+
if (err >= 400 && log_verbose) {
|
|
807
|
+
log(`[error] request failed: ${err}; ${request_ip}; ${request.url}`);
|
|
723
808
|
}
|
|
724
809
|
|
|
725
810
|
response.writeHead(err, {
|
|
@@ -733,32 +818,33 @@ const request_handle = async (request, response, https) => {
|
|
|
733
818
|
let exiting = false;
|
|
734
819
|
actions.halt = async () => {
|
|
735
820
|
await Promise.all([
|
|
736
|
-
actions.http_stop(),
|
|
821
|
+
actions.http_stop && actions.http_stop(),
|
|
737
822
|
actions.https_stop && actions.https_stop(),
|
|
738
823
|
services_shutdown(),
|
|
739
|
-
]
|
|
824
|
+
]);
|
|
740
825
|
await actions.spam_save();
|
|
741
826
|
log('stopped everything');
|
|
742
827
|
}
|
|
743
828
|
actions.exit = async status => {
|
|
744
829
|
if (exiting) return;
|
|
830
|
+
exiting = true;
|
|
745
831
|
if (typeof status !== 'number') status = 0;
|
|
746
832
|
await actions.halt();
|
|
747
833
|
log('exiting...');
|
|
748
|
-
exiting = true;
|
|
749
834
|
process.exit(status);
|
|
750
835
|
}
|
|
751
836
|
|
|
752
837
|
process.on('uncaughtException', err => {
|
|
753
|
-
err = err.message || err;
|
|
754
838
|
if (typeof err === 'symbol') err = err.toString();
|
|
755
|
-
log('error uncaughtException: ' + err);
|
|
756
|
-
console.
|
|
839
|
+
log('[error] uncaughtException: ' + (err.message || err));
|
|
840
|
+
console.error(err);
|
|
841
|
+
if (exiting) process.exit(1);
|
|
757
842
|
actions.exit(1);
|
|
758
843
|
});
|
|
759
844
|
process.on('unhandledRejection', err => {
|
|
760
|
-
log('error unhandledRejection: ' + (err.message || err));
|
|
761
|
-
console.
|
|
845
|
+
log('[error] unhandledRejection: ' + (err.message || err));
|
|
846
|
+
console.error(err);
|
|
847
|
+
if (exiting) process.exit(1);
|
|
762
848
|
actions.exit(1);
|
|
763
849
|
});
|
|
764
850
|
process.on('exit', actions.exit);
|
|
@@ -776,7 +862,7 @@ await Promise.all([
|
|
|
776
862
|
fsp.stat(PATH_PUBLIC).catch(_ => null),
|
|
777
863
|
]).then(([stat_config, stat_data, stat_public]) => {
|
|
778
864
|
if (!stat_config) {
|
|
779
|
-
log('
|
|
865
|
+
log('create config template directory');
|
|
780
866
|
fs.mkdirSync(PATH_CONFIG);
|
|
781
867
|
fs.mkdirSync(PATH_CONFIG + 'ssl');
|
|
782
868
|
for (const file of 'file_type_dyns,file_type_mimes,file_type_nocompress,path_aliases,port_http,port_https,services'.split(',')) {
|
|
@@ -798,32 +884,30 @@ await Promise.all([
|
|
|
798
884
|
}
|
|
799
885
|
});
|
|
800
886
|
|
|
887
|
+
file_keep_new(PATH_CONFIG + 'services.txt', data => (
|
|
888
|
+
map_generate_bol(services, data),
|
|
889
|
+
services_list_react()
|
|
890
|
+
));
|
|
891
|
+
|
|
801
892
|
await Promise.all([
|
|
802
893
|
file_keep_new(PATH_CONFIG + 'init.js', data => {
|
|
803
894
|
if (!data) return;
|
|
804
|
-
log('[deprecated
|
|
895
|
+
log('[deprecated] run global init script');
|
|
805
896
|
try {
|
|
806
897
|
var require = custom_require;
|
|
807
|
-
eval(data);
|
|
898
|
+
(0, eval)(data);
|
|
808
899
|
}
|
|
809
900
|
catch (err) {
|
|
810
|
-
log('error
|
|
901
|
+
log('[error] init.js: ' + err.message);
|
|
811
902
|
}
|
|
812
903
|
}),
|
|
813
|
-
file_keep_new(PATH_CONFIG + 'services.txt', async data => {
|
|
814
|
-
log('load service list');
|
|
815
|
-
map_generate_bol(services, data);
|
|
816
|
-
await services_list_react();
|
|
817
|
-
}),
|
|
818
904
|
file_keep_new(PATH_CONFIG + 'file_type_mimes.txt', data => {
|
|
819
|
-
log('load file type map');
|
|
820
905
|
map_generate_equ(file_type_mimes, data);
|
|
821
906
|
if (!file_type_mimes.has('txt')) {
|
|
822
907
|
file_type_mimes.set('txt', 'text/plain; charset=utf-8');
|
|
823
908
|
}
|
|
824
909
|
}),
|
|
825
910
|
file_keep_new(PATH_CONFIG + 'path_aliases.txt', data => {
|
|
826
|
-
log('load path aliases map');
|
|
827
911
|
map_generate_equ(path_aliases, data);
|
|
828
912
|
path_aliases_templates.clear();
|
|
829
913
|
for (const [key, value] of path_aliases.entries()) {
|
|
@@ -843,38 +927,37 @@ await Promise.all([
|
|
|
843
927
|
}
|
|
844
928
|
}),
|
|
845
929
|
file_keep_new(PATH_CONFIG + 'file_type_dyns.txt', data => {
|
|
846
|
-
log('load dynamic file type list');
|
|
847
930
|
map_generate_bol(file_type_dyns, data);
|
|
848
931
|
}),
|
|
849
932
|
file_keep_new(PATH_CONFIG + 'file_type_nocompress.txt', data => {
|
|
850
|
-
log('load non-compressable file list');
|
|
851
933
|
map_generate_bol(file_type_nocompress, data);
|
|
852
934
|
}),
|
|
853
935
|
file_keep_new(PATH_CONFIG + 'file_raws.txt', data => {
|
|
854
936
|
if (!data) return;
|
|
855
|
-
log('load static file list');
|
|
856
937
|
map_generate_bol(file_raws, data);
|
|
857
938
|
}),
|
|
858
939
|
file_keep_new(PATH_CONFIG + 'file_privates.txt', data => {
|
|
859
940
|
if (!data) return;
|
|
860
|
-
log('load private file list');
|
|
861
941
|
map_generate_bol(file_privates, data);
|
|
862
942
|
}),
|
|
863
943
|
file_keep_new(PATH_CONFIG + 'file_blocks.txt', data => {
|
|
864
944
|
if (!data) return;
|
|
865
|
-
log('load blocked file list');
|
|
866
945
|
map_generate_bol(file_blocks, data);
|
|
867
946
|
}),
|
|
868
947
|
]);
|
|
869
948
|
|
|
870
949
|
let connections_count = 0;
|
|
871
|
-
const server_http = http.createServer(
|
|
872
|
-
(request, response) => request_handle(request, response, false)
|
|
873
|
-
);
|
|
874
950
|
let http_status = false;
|
|
875
951
|
let http_status_target = false;
|
|
952
|
+
let http_listened_resolve = null;
|
|
876
953
|
const http_connections = new Map;
|
|
877
|
-
|
|
954
|
+
const server_http = http.createServer(
|
|
955
|
+
(request, response) => request_handle(request, response, false)
|
|
956
|
+
);
|
|
957
|
+
server_http.on('error', err => {
|
|
958
|
+
log('[error] http: ' + err.message);
|
|
959
|
+
http_listened_resolve && http_listened_resolve();
|
|
960
|
+
});
|
|
878
961
|
server_http.on('connection', connection => {
|
|
879
962
|
const id = ++connections_count;
|
|
880
963
|
http_connections.set(id, connection);
|
|
@@ -883,71 +966,67 @@ server_http.on('connection', connection => {
|
|
|
883
966
|
});
|
|
884
967
|
});
|
|
885
968
|
|
|
886
|
-
actions.http_start = () => {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
log('
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
actions.http_restart = () => {
|
|
898
|
-
if (!http_status) actions.http_start();
|
|
899
|
-
else if (http_status_target) {
|
|
900
|
-
http_status_target = false;
|
|
901
|
-
log('http is restarting...');
|
|
902
|
-
server_http.close(() => {
|
|
903
|
-
http_status = false;
|
|
904
|
-
log('http stopped');
|
|
905
|
-
actions.http_start();
|
|
906
|
-
});
|
|
969
|
+
actions.http_start = async () => {
|
|
970
|
+
await actions.http_stop();
|
|
971
|
+
http_status_target = true;
|
|
972
|
+
log('start http: http://localhost:' + port_http);
|
|
973
|
+
await new Promise(resolve => server_http.listen(port_http, http_listened_resolve = resolve));
|
|
974
|
+
if (http_listened_resolve) http_listened_resolve = null;
|
|
975
|
+
else{
|
|
976
|
+
http_status = true;
|
|
977
|
+
if (log_verbose) log('started http');
|
|
907
978
|
}
|
|
908
979
|
}
|
|
909
980
|
actions.http_stop = async () => {
|
|
910
|
-
if (!http_status_target
|
|
981
|
+
if (!http_status_target) return;
|
|
911
982
|
http_status_target = false;
|
|
912
|
-
log('http
|
|
983
|
+
log('stop http');
|
|
984
|
+
const kill_timeout = setTimeout(actions.http_kill, 5e3);
|
|
913
985
|
await new Promise(resolve => server_http.close(resolve));
|
|
986
|
+
clearTimeout(kill_timeout);
|
|
914
987
|
http_status = false;
|
|
915
|
-
log('http
|
|
988
|
+
if (log_verbose) log('stopped http');
|
|
916
989
|
}
|
|
917
|
-
actions.http_kill = () => {
|
|
918
|
-
if (http_status_target
|
|
919
|
-
log('
|
|
920
|
-
|
|
990
|
+
actions.http_kill = async () => {
|
|
991
|
+
if (http_status_target) return;
|
|
992
|
+
log('kill http');
|
|
993
|
+
await Promise.all(
|
|
994
|
+
Array.from(http_connections.values())
|
|
995
|
+
.map(connection => connection.destroy())
|
|
996
|
+
);
|
|
997
|
+
if (log_verbose) log('killed http');
|
|
921
998
|
http_connections.clear();
|
|
922
999
|
}
|
|
923
1000
|
|
|
924
1001
|
file_keep_new(PATH_CONFIG + 'port_http.txt', data => {
|
|
925
|
-
log('load http port number');
|
|
926
1002
|
if (
|
|
927
1003
|
!data ||
|
|
928
1004
|
isNaN(data = Number(data)) ||
|
|
929
1005
|
!number_check_uint(data)
|
|
930
1006
|
) {
|
|
931
|
-
log('error: invalid
|
|
1007
|
+
log('[error] http: invalid port number');
|
|
932
1008
|
}
|
|
933
1009
|
else if (data !== port_http) {
|
|
934
1010
|
port_http = data;
|
|
935
|
-
actions.
|
|
1011
|
+
actions.http_start();
|
|
936
1012
|
}
|
|
937
1013
|
});
|
|
938
1014
|
|
|
939
1015
|
try {
|
|
940
1016
|
const https_key = fs.readFileSync(PATH_CONFIG + 'ssl/domain.key');
|
|
941
1017
|
const https_cert = fs.readFileSync(PATH_CONFIG + 'ssl/chained.pem');
|
|
1018
|
+
let https_status = false;
|
|
1019
|
+
let https_status_target = false;
|
|
1020
|
+
let https_listened_resolve = null;
|
|
1021
|
+
const https_connections = new Map;
|
|
942
1022
|
const server_https = require('https').createServer(
|
|
943
1023
|
{key: https_key, cert: https_cert},
|
|
944
1024
|
(request, response) => request_handle(request, response, true)
|
|
945
1025
|
);
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1026
|
+
server_https.on('error', err => {
|
|
1027
|
+
log('[error] https: ' + err.message);
|
|
1028
|
+
https_listened_resolve && https_listened_resolve();
|
|
1029
|
+
});
|
|
951
1030
|
server_https.on('connection', connection => {
|
|
952
1031
|
const id = ++connections_count;
|
|
953
1032
|
https_connections.set(id, connection);
|
|
@@ -956,61 +1035,53 @@ try {
|
|
|
956
1035
|
});
|
|
957
1036
|
});
|
|
958
1037
|
|
|
959
|
-
actions.https_start = () => {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
log('
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
actions.https_restart = () => {
|
|
971
|
-
if (!https_status) actions.https_start();
|
|
972
|
-
else if (https_status_target) {
|
|
973
|
-
https_status_target = false;
|
|
974
|
-
log('https is restarting...');
|
|
975
|
-
server_https.close(function () {
|
|
976
|
-
https_status = false;
|
|
977
|
-
log('https stopped');
|
|
978
|
-
actions.https_start();
|
|
979
|
-
});
|
|
1038
|
+
actions.https_start = async () => {
|
|
1039
|
+
await actions.https_stop();
|
|
1040
|
+
https_status_target = true;
|
|
1041
|
+
log('start https: https://localhost:' + port_https);
|
|
1042
|
+
await new Promise(resolve => server_https.listen(port_https, https_listened_resolve = resolve));
|
|
1043
|
+
if (https_listened_resolve) https_listened_resolve = null;
|
|
1044
|
+
else{
|
|
1045
|
+
https_status = true;
|
|
1046
|
+
if (log_verbose) log('started https');
|
|
980
1047
|
}
|
|
981
1048
|
}
|
|
982
1049
|
actions.https_stop = async () => {
|
|
983
|
-
if (!https_status_target
|
|
1050
|
+
if (!https_status_target) return;
|
|
984
1051
|
https_status_target = false;
|
|
985
|
-
log('https
|
|
1052
|
+
log('stop https');
|
|
1053
|
+
const kill_timeout = setTimeout(actions.https_kill, 5000);
|
|
986
1054
|
await new Promise(resolve => server_https.close(resolve));
|
|
1055
|
+
clearTimeout(kill_timeout);
|
|
987
1056
|
https_status = false;
|
|
988
|
-
log('https
|
|
1057
|
+
if (log_verbose) log('stopped https');
|
|
989
1058
|
}
|
|
990
|
-
actions.https_kill = () => {
|
|
991
|
-
if (https_status_target
|
|
992
|
-
log('
|
|
993
|
-
|
|
1059
|
+
actions.https_kill = async () => {
|
|
1060
|
+
if (https_status_target) return;
|
|
1061
|
+
log('kill https');
|
|
1062
|
+
await Promise.all(
|
|
1063
|
+
Array.from(https_connections.values()).map(connection => connection.destroy())
|
|
1064
|
+
);
|
|
1065
|
+
if (log_verbose) log('killed https');
|
|
994
1066
|
https_connections.clear();
|
|
995
1067
|
}
|
|
996
1068
|
|
|
997
1069
|
file_keep_new(PATH_CONFIG + 'port_https.txt', data => {
|
|
998
|
-
log('load https port number');
|
|
999
1070
|
if (
|
|
1000
1071
|
!data ||
|
|
1001
1072
|
isNaN(data = Number(data)) ||
|
|
1002
1073
|
!number_check_uint(data)
|
|
1003
1074
|
) {
|
|
1004
|
-
log('error: invalid
|
|
1075
|
+
log('[error] https: invalid port number');
|
|
1005
1076
|
}
|
|
1006
1077
|
else if (data !== port_https) {
|
|
1007
1078
|
port_https = data;
|
|
1008
|
-
actions.
|
|
1079
|
+
actions.https_start();
|
|
1009
1080
|
}
|
|
1010
1081
|
});
|
|
1011
1082
|
}
|
|
1012
1083
|
catch (err) {
|
|
1013
|
-
log('https
|
|
1084
|
+
if (log_verbose) log('https: no cert, disabled');
|
|
1014
1085
|
}
|
|
1015
1086
|
|
|
1016
1087
|
})();
|