| import JSZip from "jszip"; |
|
|
| export interface ClonedPageAsset { |
| rawUrl: string; |
| localPath: string; |
| type: "css" | "js" | "image"; |
| content?: string; |
| } |
|
|
| |
| |
| |
| export function cleanHtmlString(html: string): string { |
| let cleaned = html; |
|
|
| |
| cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, ""); |
|
|
| |
| |
| cleaned = cleaned.replace(/<script[^>]*>[\s\S]*?(googletagmanager|google-analytics|connect\.facebook\.net|hotjar|analytics\.js)[\s\S]*?<\/script>/gi, ""); |
|
|
| |
| cleaned = cleaned.replace(/<img[^>]*src="[^"]*(pixel|track|doubleclick|googleads)[^"]*"[^>]*>/gi, ""); |
|
|
| return cleaned; |
| } |
|
|
| |
| |
| |
| export function resolveRelativeUrl(baseUrl: string, relativePath: string): string { |
| try { |
| const base = new URL(baseUrl.startsWith("http") ? baseUrl : `https://${baseUrl}`); |
| const resolved = new URL(relativePath, base); |
| return resolved.toString(); |
| } catch (e) { |
| return relativePath; |
| } |
| } |
|
|
| |
| |
| |
| export async function performWebsiteClone(targetUrl: string): Promise<{ |
| success: boolean; |
| html: string; |
| css: string; |
| js: string; |
| size: string; |
| error?: string; |
| }> { |
| try { |
| |
| const response = await fetch(`/api/proxy?url=${encodeURIComponent(targetUrl)}`); |
| if (!response.ok) { |
| throw new Error(`تعذر تحميل الموقع. رمز الخطأ: ${response.status}`); |
| } |
|
|
| const rawHtml = await response.text(); |
| const cleanedHtml = cleanHtmlString(rawHtml); |
|
|
| |
| const parser = new DOMParser(); |
| const doc = parser.parseFromString(cleanedHtml, "text/html"); |
|
|
| |
| const stylesheetLinks = Array.from(doc.querySelectorAll("link[rel='stylesheet']")); |
| let consolidatedColorsText = "/* SiteClone Pro Bundled CSS */\n"; |
| |
| |
| for (let i = 0; i < Math.min(stylesheetLinks.length, 3); i++) { |
| const link = stylesheetLinks[i]; |
| const href = link?.getAttribute("href"); |
| if (href) { |
| const fullCssUrl = resolveRelativeUrl(targetUrl, href); |
| try { |
| const cssRes = await fetch(`/api/proxy?url=${encodeURIComponent(fullCssUrl)}`); |
| if (cssRes.ok) { |
| const cssText = await cssRes.text(); |
| consolidatedColorsText += `\n/* Source: ${href} */\n${cssText}\n`; |
| |
| link.setAttribute("href", "styles.css"); |
| } |
| } catch { |
| |
| } |
| } |
| } |
|
|
| |
| const scriptTags = Array.from(doc.querySelectorAll("script[src]")); |
| let consolidatedJsText = "/* SiteClone Pro Bundled JS */\nconsole.log('SiteClone Pro V20 initialized.');\n"; |
|
|
| for (let i = 0; i < Math.min(scriptTags.length, 3); i++) { |
| const script = scriptTags[i]; |
| const src = script?.getAttribute("src"); |
| if (src && !src.includes("analytics") && !src.includes("pixel")) { |
| const fullJsUrl = resolveRelativeUrl(targetUrl, src); |
| try { |
| const jsRes = await fetch(`/api/proxy?url=${encodeURIComponent(fullJsUrl)}`); |
| if (jsRes.ok) { |
| const jsText = await jsRes.text(); |
| consolidatedJsText += `\n/* Source: ${src} */\n${jsText}\n`; |
| |
| script.setAttribute("src", "app.js"); |
| } |
| } catch { |
| |
| } |
| } |
| } |
|
|
| |
| if (stylesheetLinks.length > 0 && !doc.querySelector("link[href='styles.css']")) { |
| const cssLink = doc.createElement("link"); |
| cssLink.setAttribute("rel", "stylesheet"); |
| cssLink.setAttribute("href", "styles.css"); |
| doc.head.appendChild(cssLink); |
| } |
| if (scriptTags.length > 0 && !doc.querySelector("script[src='app.js']")) { |
| const jsScript = doc.createElement("script"); |
| jsScript.setAttribute("src", "app.js"); |
| doc.body.appendChild(jsScript); |
| } |
|
|
| const modifiedHtml = doc.documentElement.outerHTML; |
|
|
| |
| const totalBytes = new Blob([modifiedHtml, consolidatedColorsText, consolidatedJsText]).size; |
| const sizeKb = (totalBytes / 1024).toFixed(1); |
|
|
| return { |
| success: true, |
| html: modifiedHtml, |
| css: consolidatedColorsText, |
| js: consolidatedJsText, |
| size: `${sizeKb} KB` |
| }; |
| } catch (err: any) { |
| console.error("Cloning error:", err); |
| return { |
| success: false, |
| html: "", |
| css: "", |
| js: "", |
| size: "0 KB", |
| error: err?.message || String(err) |
| }; |
| } |
| } |
|
|
| |
| |
| |
| export async function downloadCloneAsZip(clone: { name: string; html: string; css: string; js: string }) { |
| const zip = new JSZip(); |
|
|
| |
| zip.file("index.html", clone.html); |
| zip.file("styles.css", clone.css); |
| zip.file("app.js", clone.js); |
|
|
| |
| const content = await zip.generateAsync({ type: "blob" }); |
| |
| |
| const url = URL.createObjectURL(content); |
| const a = document.createElement("a"); |
| a.href = url; |
| a.download = `${clone.name.replace(/\s+/g, "_")}_siteclone_pro.zip`; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| } |
|
|