Skip to content

Commit 69a8e1c

Browse files
committed
feat: enhance logging and update to version 1.0.11
1 parent 3c60e77 commit 69a8e1c

File tree

2 files changed

+108
-45
lines changed

2 files changed

+108
-45
lines changed

index.js

Lines changed: 107 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,115 +6,178 @@ const path = require('path');
66
const https = require('https');
77
const readline = require('readline');
88

9+
const colors = {
10+
reset: '\x1b[0m',
11+
info: '\x1b[38;2;120;220;255m',
12+
warn: '\x1b[38;2;255;200;87m',
13+
success: '\x1b[38;2;0;255;170m',
14+
error: '\x1b[38;2;255;120;140m',
15+
highlight: '\x1b[38;2;220;180;255m'
16+
};
17+
18+
const tags = {
19+
info: '[INFO]',
20+
warn: '[WARN]',
21+
success: '[DONE]',
22+
error: '[FAIL]',
23+
missing: '[MISSING]'
24+
};
25+
26+
const logger = {
27+
info(message) {
28+
console.log(`${colors.info}${tags.info} ${message}${colors.reset}`);
29+
},
30+
warn(message) {
31+
console.warn(`${colors.warn}${tags.warn} ${message}${colors.reset}`);
32+
},
33+
success(message) {
34+
console.log(`${colors.success}${tags.success} ${message}${colors.reset}`);
35+
},
36+
error(message) {
37+
console.error(`${colors.error}${tags.error} ${message}${colors.reset}`);
38+
},
39+
missing(file, url, destination) {
40+
console.log(`${colors.highlight}${tags.missing} ${file}${colors.reset}`);
41+
console.log(`${colors.info}Source URL: ${url}${colors.reset}`);
42+
console.log(`${colors.info}Save To: ${destination}${colors.reset}`);
43+
}
44+
};
45+
46+
const promptArrow = '\x1b[38;2;0;200;255m\u2192\x1b[0m';
47+
const promptLabel = '\x1b[37m';
48+
const promptValue = '\x1b[38;2;0;255;255m';
49+
950
const userInputReader = readline.createInterface({
1051
input: process.stdin,
11-
output: process.stdout,
52+
output: process.stdout
1253
});
1354

1455
let port = parseInt(process.argv[2] || '4000', 10);
1556
if (isNaN(port) || port < 1 || port > 65535) {
16-
console.error('端口号无效,使用默认端口 4000');
57+
logger.warn('Invalid port number. Using default port 4000');
1758
port = 4000;
1859
}
1960

20-
const promptArrow = '\x1b[38;2;0;200;255m\u2192\x1b[0m';
21-
const promptLabel = '\x1b[37m';
22-
const promptValue = '\x1b[38;2;0;255;255m';
23-
24-
console.log(`${promptArrow}${promptLabel}Local server running at: ${promptValue}http://127.0.0.1:${port}\x1b[0m`);
61+
logger.info(`Local server running at http://127.0.0.1:${port}`);
2562

2663
function streamDownload(url, filePath) {
2764
return new Promise((resolve, reject) => {
2865
const fileStream = fs.createWriteStream(filePath);
2966
const request = https.get(url, (response) => {
3067
if (response.statusCode !== 200) {
3168
fs.unlink(filePath, () => {});
32-
reject(new Error(`Request failed, status code: ${response.statusCode}`));
69+
reject(new Error(`HTTP ${response.statusCode}`));
3370
return;
3471
}
3572
response.pipe(fileStream);
3673
});
74+
3775
fileStream.on('finish', () => {
3876
fileStream.close(() => {
39-
const stats = fs.statSync(filePath);
40-
if (stats.size === 0) {
41-
fs.unlink(filePath, () => {});
42-
reject(new Error('Downloaded file is empty'));
43-
} else {
44-
resolve(stats.size);
77+
try {
78+
const stats = fs.statSync(filePath);
79+
if (stats.size === 0) {
80+
fs.unlink(filePath, () => {});
81+
reject(new Error('received empty file'));
82+
} else {
83+
resolve(stats.size);
84+
}
85+
} catch (err) {
86+
reject(new Error(`stat error: ${err.message}`));
4587
}
4688
});
4789
});
90+
4891
request.on('error', (err) => {
4992
fs.unlink(filePath, () => {});
50-
reject(new Error(`Request error: ${err.message}`));
93+
reject(new Error(`request error: ${err.message}`));
5194
});
95+
5296
fileStream.on('error', (err) => {
5397
fs.unlink(filePath, () => {});
54-
reject(new Error(`File stream error: ${err.message}`));
98+
reject(new Error(`file stream error: ${err.message}`));
5599
});
56100
});
57101
}
58102

59103
async function downloadFileWithRetries(url, filePath, maxRetries = 3) {
60104
fs.mkdirSync(path.dirname(filePath), { recursive: true });
61105
if (fs.existsSync(filePath)) {
62-
console.log(`文件已存在,跳过下载: ${filePath}`);
106+
logger.info(`Skip download, file already exists → ${filePath}`);
63107
return;
64108
}
109+
65110
for (let attempt = 1; attempt <= maxRetries; attempt++) {
66111
try {
67112
const fileSize = await streamDownload(url, filePath);
68-
console.log(`下载完成: ${filePath} (${fileSize} bytes)`);
113+
logger.success(`Downloaded ${filePath} (${fileSize} bytes)`);
69114
return;
70115
} catch (error) {
71-
console.log(`第 ${attempt}/${maxRetries} 次尝试失败: ${filePath}: ${error.message}`);
72-
if (attempt === maxRetries) {
73-
console.error(`重试 ${maxRetries} 次后,下载失败: ${filePath}`);
116+
if (attempt < maxRetries) {
117+
logger.warn(`Retry ${attempt}/${maxRetries} for ${filePath}${error.message} (${url})`);
118+
} else {
119+
logger.error(`Download failed for ${filePath} from ${url} after ${maxRetries} retries: ${error.message}`);
74120
}
75121
}
76122
}
77123
}
78124

79125
userInputReader.question(`${promptArrow}${promptLabel}请输入资源所在的根 URL: \x1b[0m`, (baseUrl) => {
80-
console.log(`${promptArrow}${promptLabel}根 URL 已设置为: ${promptValue}${baseUrl}\x1b[0m`);
126+
logger.info(`根 URL 已设置为: ${baseUrl}`);
81127
userInputReader.close();
82128

83-
console.log('\n--------------------------------------------------------');
84-
console.log(`${promptArrow}${promptLabel}监控已启动。`);
85-
console.log(`${promptArrow}${promptLabel}下一步: 请用浏览器打开 ${promptValue}http://127.0.0.1:${port}${promptLabel}`);
86-
console.log(`${promptArrow}${promptLabel}脚本将会自动从 ${promptValue}${baseUrl}${promptLabel} 下载任何缺失的资源。`);
87-
console.log('--------------------------------------------------------\n');
129+
const divider = '─'.repeat(56);
130+
const introLines = [
131+
'监控已启动',
132+
`下一步: 打开 http://127.0.0.1:${port}`,
133+
`缺失的资源会自动从 ${baseUrl} 拉取`
134+
];
135+
136+
console.log();
137+
logger.info(divider);
138+
introLines.forEach((line) => logger.info(line));
139+
logger.info(divider);
140+
console.log();
88141

89142
const httpServerProcess = exec(`npx http-server -p ${port}`);
90143

91144
httpServerProcess.stdout.on('data', async (data) => {
92145
const output = data.toString();
93146
const missingFileRegex = /"GET (\/.*?)" Error \(404\):/g;
94-
const missingFileMatches = [...output.matchAll(missingFileRegex)];
95-
if (missingFileMatches.length > 0) {
96-
const ignoreList = [
97-
'/.well-known/appspecific/com.chrome.devtools.json',
98-
];
99-
for (const match of missingFileMatches) {
100-
const encodedFilePath = match[1].replace(/"/g, '');
101-
const decodedFilePath = decodeURIComponent(encodedFilePath);
102-
if (ignoreList.includes(decodedFilePath)) {
103-
continue;
104-
}
105-
console.log('检测到缺失文件:', decodedFilePath);
106-
const localFilePath = path.join(process.cwd(), decodedFilePath);
107-
const remoteFileUrl = `${baseUrl}${encodedFilePath}`;
108-
await downloadFileWithRetries(remoteFileUrl, localFilePath);
147+
const matches = [...output.matchAll(missingFileRegex)];
148+
149+
if (matches.length === 0) {
150+
return;
151+
}
152+
153+
const ignoreList = [
154+
'/.well-known/appspecific/com.chrome.devtools.json'
155+
];
156+
157+
for (const match of matches) {
158+
const encodedPath = match[1].replace(/"/g, '');
159+
const decodedPath = decodeURIComponent(encodedPath);
160+
if (ignoreList.includes(decodedPath)) {
161+
continue;
109162
}
163+
164+
const localFilePath = path.join(process.cwd(), decodedPath);
165+
const remoteFileUrl = `${baseUrl}${encodedPath}`;
166+
167+
logger.missing(decodedPath, remoteFileUrl, localFilePath);
168+
await downloadFileWithRetries(remoteFileUrl, localFilePath);
110169
}
111170
});
112171

113172
httpServerProcess.stderr.on('data', (data) => {
114-
console.error('http-server 错误:', data.toString());
173+
logger.error(`http-server stderr: ${data.toString().trim()}`);
174+
});
175+
176+
httpServerProcess.on('error', (err) => {
177+
logger.error(`Unable to start http-server: ${err.message}`);
115178
});
116179

117180
httpServerProcess.on('close', (code) => {
118-
console.log(`http-server 进程已退出,退出码: ${code}`);
181+
logger.info(`http-server process exited with code ${code}`);
119182
});
120183
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "resource-save-script",
3-
"version": "1.0.10",
3+
"version": "1.0.11",
44
"description": "HTTP server with auto-download capability",
55
"homepage": "https://github.com/luckfunc/resource-save-script#readme",
66
"bugs": {

0 commit comments

Comments
 (0)