-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwebfreeze.js
138 lines (123 loc) · 4.19 KB
/
webfreeze.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* WebFreeze: A tool to capture and serve static versions of dynamic web pages
*
* This script uses Puppeteer to capture a web page and its resources, saving them locally.
* It can then serve these local files, effectively "freezing" the web page at a specific point in time.
*/
const puppeteer = require("puppeteer");
const fs = require("fs");
const fsPromises = require("fs").promises;
const path = require("path");
const https = require("https");
const URL = require("url");
// Configuration
const SITE_URL = "https://example.com";
const SAVE_DIRECTORY = "./files";
const isDownloadMode = false; // true: download and save, false: use local files or block
/**
* Downloads a file from a given URL and saves it to the specified path
* @param {string} url - The URL of the file to download
* @param {string} filePath - The local path where the file will be saved
* @returns {Promise<void>}
*/
async function downloadFile(url, filePath) {
return new Promise((resolve, reject) => {
https
.get(url, (response) => {
if (response.statusCode === 200) {
const fileStream = fs.createWriteStream(filePath);
response.pipe(fileStream);
fileStream.on("finish", () => {
fileStream.close();
console.log(`Downloaded: ${url}`);
resolve();
});
} else {
reject(`Failed to download ${url}: ${response.statusCode}`);
}
})
.on("error", reject);
});
}
/**
* Generates a local file path for a given URL
* @param {string} url - The URL to convert to a local path
* @returns {string} The local file path
*/
function getFilePath(url) {
const parsedUrl = new URL.URL(url);
let filePath = parsedUrl.pathname;
// Handle paths starting with _next
if (filePath.startsWith("/_next")) {
filePath = filePath.replace(/^\//, "");
}
return path.join(SAVE_DIRECTORY, filePath);
}
/**
* Intercepts requests and handles them based on the current mode
* @param {puppeteer.Page} page - The Puppeteer page object
*/
async function interceptRequests(page) {
await page.setRequestInterception(true);
page.on("request", async (request) => {
const url = request.url();
const resourceType = request.resourceType();
if (["stylesheet", "script", "image"].includes(resourceType)) {
const filePath = getFilePath(url);
if (isDownloadMode) {
// Download mode: Download and save files
try {
await fsPromises.access(filePath);
request.continue();
} catch (error) {
try {
const directoryPath = path.dirname(filePath);
await fsPromises.mkdir(directoryPath, { recursive: true });
await downloadFile(url, filePath);
request.continue();
} catch (downloadError) {
console.error(`Failed to download ${url}: ${downloadError}`);
request.continue();
}
}
} else {
// Block mode: Use local files or block requests
try {
await fsPromises.access(filePath);
const content = await fsPromises.readFile(filePath);
console.log(`Serving: ${filePath}`);
request.respond({
status: 200,
contentType: request.headers()["content-type"],
body: content,
});
} catch (error) {
console.log(`Blocked: ${filePath}`);
request.abort();
}
}
} else {
request.continue();
}
});
}
/**
* Main function to run the script
*/
async function main() {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await interceptRequests(page);
console.log(`Navigating to ${SITE_URL}`);
await page.goto(SITE_URL, { waitUntil: "networkidle0" });
// Save the main HTML
const content = await page.content();
const htmlPath = path.join(SAVE_DIRECTORY, "index.html");
await fsPromises.writeFile(htmlPath, content);
console.log(`Saved main HTML to ${htmlPath}`);
// Keep the browser open for inspection
console.log("Browser will remain open. Close it manually when done.");
// Uncomment the next line to close the browser automatically
// await browser.close();
}
main().catch(console.error);