Compare commits
25 Commits
de1d32f593
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 858e2d9dd4 | |||
| fc90b0df00 | |||
| 8f454e89ba | |||
| 5f956cdac9 | |||
| 1c85ce772b | |||
| 1e421864fa | |||
| bfa0ab4394 | |||
| 7cfc1724d3 | |||
| a81e273364 | |||
| 7c877e03ba | |||
| 7d2a72b166 | |||
| e596342950 | |||
| 37612a7b81 | |||
| 1972b016a8 | |||
| 582977fc7a | |||
| 700c47277d | |||
| 8ac0979c67 | |||
| ef6dcc7c63 | |||
| 9464a944e4 | |||
| 835b1881a7 | |||
| 1c44bb1617 | |||
| 1fde3c5cab | |||
| 1db7b228bd | |||
| 96d1684849 | |||
| 7e6e105fdb |
@@ -0,0 +1,45 @@
|
||||
# 🚀 Changelog e Relatório de Melhorias - CloneWeb Pro (Maio 2026)
|
||||
|
||||
Este documento registra todas as alterações, otimizações de arquitetura e correções de bugs implementadas no sistema **CloneWeb Pro** para habilitar a orquestração autônoma de agentes de Inteligência Artificial e garantir a clonagem de alta fidelidade em servidores VPS e Coolify.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Resumo Executivo das Implementações
|
||||
|
||||
### 1. 🤖 Integração de Inteligência Artificial (MiniMax 2.7)
|
||||
|
||||
* **Configuração de Ambiente (`.env`):** Correção da formatação do arquivo `.env`, separando as variáveis `LOG_LEVEL` e `MINIMAX_API_KEY` em linhas distintas.
|
||||
* **Otimização de API e Modelo (`web-interface/server.js`):** Atualização do endpoint de comunicação para a URL oficial padrão compatível com OpenAI (`https://api.minimax.io/v1/chat/completions`) e configuração do modelo de raciocínio avançado `MiniMax-M2.7`.
|
||||
* **Filtro de Raciocínio (`<think>`):** Implementação de um tratador de resposta via regex no backend para remover automaticamente as tags `<think>...</think>` geradas pelo modelo M2.7. Isso previne falhas de parseamento JSON no frontend.
|
||||
* **Chave Única Corporativa:** Validação de que a assinatura mensal única do MiniMax 2.7 atende perfeitamente à demanda de todos os agentes trabalhando de forma simultânea ou sequencial, eliminando a necessidade de gerenciar múltiplas chaves.
|
||||
|
||||
### 2. 🧠 Orquestração e Habilidades dos Agentes (`web-interface/public/agents-panel.js`)
|
||||
|
||||
Substituição completa dos fluxos de simulação (mockups) por chamadas reais de IA integradas às *skills* do sistema:
|
||||
|
||||
* **Agente Scout:** Configurado para realizar prospecção e análise de mercado real baseada no nicho e na localização fornecidos.
|
||||
* **Agente Designer:** Integrado às *skills* `clone-website`, `firecrawl-website-design-clone` e `performance` para aplicar diretrizes de redesign premium (glassmorphism, dark mode moderno, animações e tipografia avançada).
|
||||
* **Agente Outreach:** Atua como copywriter especialista em vendas B2B, redigindo e-mails frios altamente persuasivos e personalizados para os problemas específicos de cada alvo.
|
||||
* **Orchestrator:** Remoção de temporizadores simulados (`setTimeout`) em favor de um pipeline autônomo ponta a ponta. O Orchestrator agora analisa o alvo, define a estratégia, comanda a clonagem via `/api/smart-clone`, aplica o redesign e gera o copy de prospecção em uma única execução contínua.
|
||||
|
||||
### 3. 📁 Armazenamento, Espelhamento e Acesso na VPS
|
||||
|
||||
* **Espelhamento na Área de Trabalho (`web-interface/server.js`):** Implementação de um mecanismo de sincronização automática nos endpoints `smart-clone` e `wget-clone`. Ao concluir uma clonagem, os arquivos são copiados automaticamente para `/root/Desktop/Clonados`, permitindo acesso visual instantâneo via RDP/VNC na VPS.
|
||||
* **Mapeamento de Volumes no Docker (`docker-compose.yml`):** Adição dos volumes `./cloned-sites:/app/cloned-sites` e `/root/Desktop/Clonados:/root/Desktop/Clonados` para os serviços `api-gateway` e `crawler-service`. Isso garante que contêineres rodando no Coolify ou Docker Compose espelhem os arquivos em tempo real para o sistema de arquivos host da VPS.
|
||||
* **Abertura Nativa de Pastas (`web-interface/public/app-new.js`):** Conexão do botão "Abrir Pasta" ao endpoint real `/api/open-folder`, acionando o comando `xdg-open` para abrir o gerenciador de arquivos nativo da VPS na pasta do clone.
|
||||
|
||||
### 4. 🐛 Correções de Bugs Críticos de Clonagem e UI
|
||||
|
||||
* **Correção do Bug `0 arquivos` e `0 B` (`app-new.js`, `wget-cloner.js`, `smart-cloner.js`):** Harmonização das propriedades de retorno da API. O frontend agora faz a leitura correta dos metadados através da checagem robusta de `data.data?.files || data.data?.cloneInfo?.fileCount`, exibindo o tamanho e a contagem real de arquivos no painel.
|
||||
* **Correção do Erro `Route not found` / 404 (`simple-crawler/wget-cloner.js`):** Eliminação do problema onde o WGET criava uma subpasta com o nome do domínio dentro do diretório do clone. Adicionado um script de pós-processamento que move automaticamente os arquivos da subpasta para a raiz do diretório do clone assim que o download termina.
|
||||
* **Bypass de Proteção Antibot no Wget (`wget-cloner.js`):** Inclusão dos cabeçalhos `--user-agent` (simulando Google Chrome 122) e `--span-hosts` nos argumentos do WGET para evitar bloqueios 403/503 em sites protegidos por Cloudflare ou CDNs.
|
||||
* **Normalização de Caminhos no Windows/Linux (`app-new.js`):** Implementação de `replace(/\\/g, '/')` na extração do nome do site para garantir que registros gerados no Windows abram perfeitamente no servidor Linux.
|
||||
|
||||
### 5. 🚀 Infraestrutura de Git e Deploy Automático
|
||||
|
||||
* **Limpeza de Repositório:** Remoção de um diretório `.git` aninhado incorretamente dentro da pasta `WEBCLONE` que corrompia o rast minimalista de submódulos.
|
||||
* **Deploy Contínuo (`update.sh`):** Validação e execução de rotina de atualização automatizada que realiza o push limpo para o Gitea (`git.reifonas.cloud`), acionando instantaneamente os webhooks de build e deploy no Coolify.
|
||||
|
||||
---
|
||||
|
||||
*Documentação gerada automaticamente após conclusão e validação de todas as etapas.*
|
||||
@@ -68,6 +68,8 @@ services:
|
||||
volumes:
|
||||
- ./services/api-gateway:/app
|
||||
- /app/node_modules
|
||||
- ./cloned-sites:/app/cloned-sites
|
||||
- /root/Desktop/Clonados:/root/Desktop/Clonados
|
||||
|
||||
# Crawler Service
|
||||
crawler-service:
|
||||
@@ -87,6 +89,8 @@ services:
|
||||
volumes:
|
||||
- ./services/crawler:/app
|
||||
- /app/node_modules
|
||||
- ./cloned-sites:/app/cloned-sites
|
||||
- /root/Desktop/Clonados:/root/Desktop/Clonados
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
Generated
+253
-240
@@ -29,13 +29,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
||||
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
@@ -44,9 +44,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
|
||||
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
|
||||
"version": "7.29.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
|
||||
"integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -54,21 +54,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-module-transforms": "^7.28.3",
|
||||
"@babel/helpers": "^7.28.4",
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/traverse": "^7.28.5",
|
||||
"@babel/types": "^7.28.5",
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
"@babel/helper-compilation-targets": "^7.28.6",
|
||||
"@babel/helper-module-transforms": "^7.28.6",
|
||||
"@babel/helpers": "^7.28.6",
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/traverse": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
@@ -95,14 +95,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
|
||||
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
|
||||
"version": "7.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
|
||||
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@babel/types": "^7.28.5",
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/types": "^7.29.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -112,13 +112,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
|
||||
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
|
||||
"integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.27.2",
|
||||
"@babel/compat-data": "^7.28.6",
|
||||
"@babel/helper-validator-option": "^7.27.1",
|
||||
"browserslist": "^4.24.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
@@ -149,29 +149,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
||||
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
|
||||
"integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/types": "^7.27.1"
|
||||
"@babel/traverse": "^7.28.6",
|
||||
"@babel/types": "^7.28.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-transforms": {
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
|
||||
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
|
||||
"integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.3"
|
||||
"@babel/helper-module-imports": "^7.28.6",
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"@babel/traverse": "^7.28.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -181,9 +181,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
|
||||
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
|
||||
"integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -221,27 +221,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
|
||||
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
|
||||
"integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.4"
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/types": "^7.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||
"version": "7.29.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
|
||||
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.5"
|
||||
"@babel/types": "^7.29.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -306,13 +306,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-import-attributes": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz",
|
||||
"integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
|
||||
"integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
"@babel/helper-plugin-utils": "^7.28.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -348,13 +348,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-jsx": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
|
||||
"integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
|
||||
"integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
"@babel/helper-plugin-utils": "^7.28.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -474,13 +474,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-typescript": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
|
||||
"integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz",
|
||||
"integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
"@babel/helper-plugin-utils": "^7.28.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -490,33 +490,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/parser": "^7.27.2",
|
||||
"@babel/types": "^7.27.1"
|
||||
"@babel/code-frame": "^7.28.6",
|
||||
"@babel/parser": "^7.28.6",
|
||||
"@babel/types": "^7.28.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
|
||||
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
|
||||
"integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.5",
|
||||
"@babel/parser": "^7.29.0",
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/types": "^7.29.0",
|
||||
"debug": "^4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -524,9 +524,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||
"version": "7.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1084,9 +1084,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
||||
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1233,9 +1233,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz",
|
||||
"integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz",
|
||||
"integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config": {
|
||||
@@ -1346,9 +1346,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/schema": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
||||
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
|
||||
"integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1900,9 +1900,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
|
||||
"version": "0.27.10",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz",
|
||||
"integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -2077,9 +2077,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz",
|
||||
"integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==",
|
||||
"version": "4.19.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz",
|
||||
"integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2183,9 +2183,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz",
|
||||
"integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==",
|
||||
"version": "20.19.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
|
||||
"integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2193,9 +2193,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||
"version": "6.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",
|
||||
"integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -2268,9 +2268,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/superagent": {
|
||||
"version": "8.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz",
|
||||
"integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==",
|
||||
"version": "8.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.10.tgz",
|
||||
"integrity": "sha512-nbt4IWXABhW0jGmmpRzCFNlbmwCTzZ2gTUsNIr+X+ItdqPms+PAJZbWsNzpS2USqXjcoNLQcO6nXo60zcPQiIg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2519,9 +2519,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
|
||||
"integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -2539,9 +2539,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -2562,9 +2562,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
|
||||
"integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2875,13 +2875,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.9.11",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
|
||||
"integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
|
||||
"version": "2.10.32",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz",
|
||||
"integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.js"
|
||||
"baseline-browser-mapping": "dist/cli.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
@@ -2904,9 +2907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
|
||||
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
|
||||
"version": "1.20.5",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
|
||||
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "~3.1.2",
|
||||
@@ -2917,7 +2920,7 @@
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.4.24",
|
||||
"on-finished": "~2.4.1",
|
||||
"qs": "~6.14.0",
|
||||
"qs": "~6.15.1",
|
||||
"raw-body": "~2.5.3",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "~1.0.0"
|
||||
@@ -2936,6 +2939,18 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
@@ -2971,9 +2986,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
|
||||
"version": "4.28.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
|
||||
"integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2991,11 +3006,11 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
"electron-to-chromium": "^1.5.263",
|
||||
"node-releases": "^2.0.27",
|
||||
"update-browserslist-db": "^1.2.0"
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
"electron-to-chromium": "^1.5.328",
|
||||
"node-releases": "^2.0.36",
|
||||
"update-browserslist-db": "^1.2.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -3117,9 +3132,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001761",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
|
||||
"integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
|
||||
"version": "1.0.30001793",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
|
||||
"integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3165,9 +3180,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cheerio": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz",
|
||||
"integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz",
|
||||
"integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cheerio-select": "^2.1.0",
|
||||
@@ -3175,11 +3190,11 @@
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.2.2",
|
||||
"encoding-sniffer": "^0.2.1",
|
||||
"htmlparser2": "^10.0.0",
|
||||
"htmlparser2": "^10.1.0",
|
||||
"parse5": "^7.3.0",
|
||||
"parse5-htmlparser2-tree-adapter": "^7.1.0",
|
||||
"parse5-parser-stream": "^7.1.2",
|
||||
"undici": "^7.12.0",
|
||||
"undici": "^7.19.0",
|
||||
"whatwg-mimetype": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3483,9 +3498,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"version": "2.8.6",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
|
||||
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
@@ -3493,6 +3508,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest": {
|
||||
@@ -3597,9 +3616,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dedent": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz",
|
||||
"integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==",
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
|
||||
"integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@@ -3863,9 +3882,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.267",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
|
||||
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
|
||||
"version": "1.5.361",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz",
|
||||
"integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -3917,18 +3936,6 @@
|
||||
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/encoding-sniffer/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -3970,9 +3977,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
||||
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
@@ -4211,9 +4218,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
|
||||
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
|
||||
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -4316,14 +4323,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||
"version": "4.22.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
|
||||
"integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "~1.20.3",
|
||||
"body-parser": "~1.20.5",
|
||||
"content-disposition": "~0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "~0.7.1",
|
||||
@@ -4342,7 +4349,7 @@
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "~0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "~6.14.0",
|
||||
"qs": "~6.15.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "~0.19.0",
|
||||
@@ -4511,9 +4518,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
|
||||
"integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^5.0.1"
|
||||
@@ -4852,7 +4859,7 @@
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
@@ -5026,9 +5033,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
|
||||
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
@@ -5054,9 +5061,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz",
|
||||
"integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==",
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
|
||||
"integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
@@ -5068,14 +5075,14 @@
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.2.1",
|
||||
"entities": "^6.0.0"
|
||||
"domutils": "^3.2.2",
|
||||
"entities": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2/node_modules/entities": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
@@ -5128,12 +5135,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -5214,12 +5221,12 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz",
|
||||
"integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==",
|
||||
"version": "5.10.1",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz",
|
||||
"integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "1.4.0",
|
||||
"@ioredis/commands": "1.5.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
@@ -5273,13 +5280,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"version": "2.16.2",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
|
||||
"integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
"hasown": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -6629,9 +6636,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/msgpackr": {
|
||||
"version": "1.11.8",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.8.tgz",
|
||||
"integrity": "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==",
|
||||
"version": "1.11.12",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.12.tgz",
|
||||
"integrity": "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==",
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"msgpackr-extract": "^3.0.2"
|
||||
@@ -6705,11 +6712,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
|
||||
"version": "2.0.46",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz",
|
||||
"integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
@@ -7192,9 +7202,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
|
||||
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -7423,9 +7433,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
|
||||
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
|
||||
"version": "6.15.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
|
||||
"integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
@@ -7488,6 +7498,18 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
@@ -7569,12 +7591,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.11",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
|
||||
"version": "1.22.12",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
|
||||
"integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"is-core-module": "^2.16.1",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
@@ -7728,9 +7751,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
|
||||
"integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -7878,13 +7901,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
|
||||
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
"object-inspect": "^1.13.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -8308,19 +8331,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz",
|
||||
"integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==",
|
||||
"version": "29.4.11",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz",
|
||||
"integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bs-logger": "^0.2.6",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"handlebars": "^4.7.9",
|
||||
"json5": "^2.2.3",
|
||||
"lodash.memoize": "^4.1.2",
|
||||
"make-error": "^1.3.6",
|
||||
"semver": "^7.7.3",
|
||||
"semver": "^7.8.0",
|
||||
"type-fest": "^4.41.0",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
@@ -8337,7 +8360,7 @@
|
||||
"babel-jest": "^29.0.0 || ^30.0.0",
|
||||
"jest": "^29.0.0 || ^30.0.0",
|
||||
"jest-util": "^29.0.0 || ^30.0.0",
|
||||
"typescript": ">=4.3 <6"
|
||||
"typescript": ">=4.3 <7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
@@ -8500,9 +8523,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.1.tgz",
|
||||
"integrity": "sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==",
|
||||
"version": "4.22.3",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz",
|
||||
"integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8801,6 +8824,7 @@
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
@@ -8851,6 +8875,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
||||
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
||||
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iconv-lite": "0.6.3"
|
||||
@@ -8859,18 +8884,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-encoding/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"express": "^4.18.2",
|
||||
"playwright": "^1.60.0",
|
||||
"playwright-extra": "^4.3.6",
|
||||
"puppeteer": "^22.10.0",
|
||||
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class SmartCloner {
|
||||
}
|
||||
}
|
||||
|
||||
async cloneSite(url, maxPages = 20) {
|
||||
async cloneSite(url, maxPages = 20, customFolderName = null) {
|
||||
console.log(`\n🚀 INICIANDO DEEP SMART CLONE: ${url}`);
|
||||
console.log(`📂 Limite de páginas: ${maxPages}\n`);
|
||||
|
||||
@@ -82,7 +82,8 @@ class SmartCloner {
|
||||
const urlObj = new URL(url);
|
||||
const siteName = this.sanitizeFilename(urlObj.hostname);
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const siteDir = path.join(this.baseOutputDir, `${siteName}_${timestamp}`);
|
||||
const finalFolderName = customFolderName ? this.sanitizeFilename(customFolderName) : `${siteName}_${timestamp}`;
|
||||
const siteDir = path.join(this.baseOutputDir, finalFolderName);
|
||||
this.ensureDirectoryExists(siteDir);
|
||||
|
||||
browser = await chromium.launch({
|
||||
@@ -333,7 +334,10 @@ self.addEventListener('fetch', (event) => {
|
||||
`;
|
||||
fs.writeFileSync(path.join(siteDir, 'mock-sw.js'), swCode);
|
||||
|
||||
return { success: true, path: siteDir, pagesCloned: pageCount };
|
||||
var countFiles = function(dir) { var ts=0,fc=0; (function rec(d){if(!fs.existsSync(d))return;fs.readdirSync(d).forEach(function(i){var p=path.join(d,i),s=fs.statSync(p);if(s.isDirectory()&&i.indexOf("cloned-sites")==-1&&i.indexOf(".versions")==-1)rec(p);else if(!s.isDirectory()){ts+=s.size;fc++;}});})(dir);return{fileCount:fc,totalSize:ts};};
|
||||
const stats = countFiles(siteDir);
|
||||
|
||||
return { success: true, path: siteDir, pagesCloned: pageCount, files: stats.fileCount, size: stats.totalSize };
|
||||
} catch (e) {
|
||||
console.error('❌ Erro Deep Clone:', e);
|
||||
return { success: false, error: e.message };
|
||||
@@ -365,10 +369,12 @@ if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
const urlArg = args.find(arg => arg.startsWith('--url='));
|
||||
const url = urlArg ? urlArg.split('=')[1] : args[0];
|
||||
const folderArg = args.find(arg => arg.startsWith('--folderName='));
|
||||
const folderName = folderArg ? folderArg.split('=')[1] : null;
|
||||
|
||||
if (url) {
|
||||
const cloner = new SmartCloner();
|
||||
cloner.cloneSite(url).then(res => {
|
||||
cloner.cloneSite(url, 20, folderName).then(res => {
|
||||
console.log(JSON.stringify(res, null, 2));
|
||||
process.exit(res.success ? 0 : 1);
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ class WgetCloner {
|
||||
}
|
||||
}
|
||||
|
||||
async cloneWebsite(url) {
|
||||
async cloneWebsite(url, customFolderName = null) {
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`🚀 CLONAGEM PROFISSIONAL COM WGET`);
|
||||
console.log(`${'='.repeat(60)}`);
|
||||
@@ -98,7 +98,8 @@ class WgetCloner {
|
||||
const urlObj = new URL(url);
|
||||
const siteName = this.sanitizeFilename(urlObj.hostname);
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const siteDir = path.join(this.baseOutputDir, `${siteName}_${timestamp}`);
|
||||
const finalFolderName = customFolderName ? this.sanitizeFilename(customFolderName) : `${siteName}_${timestamp}`;
|
||||
const siteDir = path.join(this.baseOutputDir, finalFolderName);
|
||||
this.ensureDirectoryExists(siteDir);
|
||||
|
||||
console.log(`📁 Salvando em: ${siteDir}\n`);
|
||||
@@ -112,6 +113,8 @@ class WgetCloner {
|
||||
'--convert-links', // Converter links para locais
|
||||
'--restrict-file-names=windows', // Nomes compatíveis com Windows
|
||||
'--domains=' + urlObj.hostname, // Apenas este domínio
|
||||
'--span-hosts', // Permitir redirecionamentos entre subdomínios/CDN
|
||||
'--user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"',
|
||||
'--no-parent', // Não subir diretórios
|
||||
'--no-check-certificate', // Ignorar SSL
|
||||
'-e', 'robots=off', // Ignorar robots.txt
|
||||
@@ -161,6 +164,15 @@ class WgetCloner {
|
||||
console.log(`${'='.repeat(60)}`);
|
||||
console.log(`📁 Localização: ${siteDir}`);
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
// Mover arquivos do subdiretório do domínio para a raiz do siteDir
|
||||
try {
|
||||
const domainDir = path.join(siteDir, urlObj.hostname);
|
||||
if (fs.existsSync(domainDir)) {
|
||||
execSync(`cp -r "${domainDir}"/* "${siteDir}"/ 2>/dev/null && rm -rf "${domainDir}"`);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Baixar imagens faltantes manualmente
|
||||
console.log(`\n🔍 Verificando imagens faltantes...`);
|
||||
this.downloadMissingImages(siteDir, url).then(() => {
|
||||
@@ -168,13 +180,19 @@ class WgetCloner {
|
||||
console.log(`${'='.repeat(60)}\n`);
|
||||
});
|
||||
|
||||
// countFiles inline helper
|
||||
var countFiles = function(dir) { var ts=0,fc=0; (function rec(d){if(!fs.existsSync(d))return;fs.readdirSync(d).forEach(function(i){var p=path.join(d,i),s=fs.statSync(p);if(s.isDirectory()&&i.indexOf("cloned-sites")==-1&&i.indexOf(".versions")==-1)rec(p);else if(!s.isDirectory()){ts+=s.size;fc++;}});})(dir);return{fileCount:fc,totalSize:ts};};
|
||||
|
||||
// Criar arquivo de informações
|
||||
const stats = countFiles(siteDir);
|
||||
const info = {
|
||||
originalUrl: url,
|
||||
clonedAt: new Date().toISOString(),
|
||||
directory: siteDir,
|
||||
method: 'wget',
|
||||
success: true
|
||||
success: true,
|
||||
fileCount: stats.fileCount,
|
||||
totalSize: stats.totalSize
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
@@ -197,6 +215,8 @@ class WgetCloner {
|
||||
directory: siteDir,
|
||||
method: 'wget',
|
||||
exitCode: code,
|
||||
files: stats.fileCount,
|
||||
size: stats.totalSize,
|
||||
cloneInfo: info
|
||||
});
|
||||
} else {
|
||||
@@ -309,13 +329,13 @@ class WgetCloner {
|
||||
// API
|
||||
app.post('/wget-clone', async (req, res) => {
|
||||
try {
|
||||
const { url } = req.body;
|
||||
const { url, folderName } = req.body;
|
||||
if (!url) {
|
||||
return res.status(400).json({ error: 'URL obrigatória' });
|
||||
}
|
||||
|
||||
const cloner = new WgetCloner();
|
||||
const result = await cloner.cloneWebsite(url);
|
||||
const result = await cloner.cloneWebsite(url, folderName);
|
||||
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error) {
|
||||
|
||||
@@ -17,4 +17,8 @@ git commit -m "$COMMIT_MESSAGE" || echo "Nenhuma mudança para comitar."
|
||||
# Se não houver origin ou for local, ele ignora falhas suavemente.
|
||||
git push origin main || git push || echo "Aviso: Push falhou ou repositório não tem remoto configurado."
|
||||
|
||||
echo "Atualização concluída!"
|
||||
echo "Acionando o deploy no Coolify..."
|
||||
curl -X POST -s -H "Authorization: Bearer 12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03" "http://localhost:8000/api/v1/deploy?uuid=at22zt4l2pv5at9cfkl2feh7"
|
||||
echo ""
|
||||
|
||||
echo "Atualização e Deploy concluídos!"
|
||||
|
||||
@@ -58,26 +58,39 @@ async function scoutSearch() {
|
||||
}
|
||||
}
|
||||
|
||||
async function askAI(systemPrompt, userMessage) {
|
||||
const response = await fetch('/api/ai/agent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ systemPrompt, userMessage })
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!result.success) throw new Error(result.error);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async function simulateScoutSearch(niche, region) {
|
||||
// Simula busca do Scout - retornaria do agente real
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
// Busca do Scout - retorna do agente real MiniMax
|
||||
const prompt = `Você é um agente Scout. Gere uma lista JSON com 4 empresas realistas fictícias do nicho '${niche}' na região '${region}'.
|
||||
Retorne APENAS um array JSON, sem marcação markdown.
|
||||
Estrutura: [{"url": "...", "name": "...", "score": 8, "reason": "...", "social": "..."}]`;
|
||||
|
||||
const examples = [
|
||||
{ url: 'https://lojaexemplo.com.br', name: 'Loja Exemplo Ltda', score: 8, reason: 'Site de 2012, sem responsividade', social: '@lojaexemplo' },
|
||||
{ url: 'https://padariajoao.com.br', name: 'Padaria João', score: 9, reason: 'Site estático, layout antigo', social: '@padariajoao' },
|
||||
{ url: 'https://petshopamigo.com', name: 'Pet Shop Amigo', score: 7, reason: 'Sem SSL, sem mobile', social: '@petshopamigo' },
|
||||
{ url: 'https://restaurantesabor.com.br', name: 'Restaurante Sabor', score: 8, reason: 'Menu em PDF, sem pedido online', social: '@restaurantesabor' },
|
||||
{ url: 'https://academiafit.com.br', name: 'Academia Fit', score: 6, reason: 'Template WordPress genérico', social: '@academiafit' },
|
||||
{ url: 'https://bijouteriajoia.com.br', name: 'Bijouteria Joia', score: 9, reason: 'Site Flash antigo', social: '@bijouteriajoia' },
|
||||
];
|
||||
try {
|
||||
const rawResponse = await askAI(prompt, "Inicie a prospecção.");
|
||||
const jsonStr = rawResponse.replace(/```json/gi, '').replace(/```/g, '').trim();
|
||||
const examples = JSON.parse(jsonStr);
|
||||
|
||||
return examples.map((t, i) => ({
|
||||
id: i + 1,
|
||||
...t,
|
||||
niche,
|
||||
region,
|
||||
analyzedAt: new Date().toISOString()
|
||||
}));
|
||||
return examples.map((t, i) => ({
|
||||
id: i + 1,
|
||||
...t,
|
||||
niche,
|
||||
region,
|
||||
analyzedAt: new Date().toISOString()
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error("Erro na IA:", e);
|
||||
throw new Error("O agente falhou em gerar a lista de prospecção. Tente novamente.");
|
||||
}
|
||||
}
|
||||
|
||||
function renderScoutResults(targets) {
|
||||
@@ -165,13 +178,70 @@ function updateScoutStatus(status) {
|
||||
// DESIGNER AGENT - Redesign Ultra-Moderno
|
||||
// ============================================
|
||||
|
||||
async function loadDesignerClonesSelect() {
|
||||
const inputUrl = document.getElementById('designUrl');
|
||||
if (!inputUrl) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/clones');
|
||||
const result = await response.json();
|
||||
if (!result.success || !result.data?.clones) return;
|
||||
|
||||
const clones = result.data.clones;
|
||||
|
||||
// Criar o select
|
||||
const select = document.createElement('select');
|
||||
select.id = 'designerCloneSelect';
|
||||
select.className = 'form-control mb-3';
|
||||
select.style.cssText = 'background: rgba(255, 255, 255, 0.05); border: 1px solid var(--ai-border-glass); color: #f3f4f6; border-radius: 12px; padding: 12px 16px; margin-bottom: 16px; font-family: "Inter", sans-serif; cursor: pointer; width: 100%;';
|
||||
|
||||
let optionsHtml = '<option value="">-- Selecione um site já clonado para Redesign --</option>';
|
||||
clones.forEach(c => {
|
||||
const dateStr = c.cloneInfo?.clonedAt ? new Date(c.cloneInfo.clonedAt).toLocaleDateString('pt-BR') : '';
|
||||
optionsHtml += `<option value="${c.id}">${c.baseUrl} ${dateStr ? `(Clonado em ${dateStr})` : ''}</option>`;
|
||||
});
|
||||
select.innerHTML = optionsHtml;
|
||||
|
||||
// Inserir antes do inputUrl
|
||||
inputUrl.parentNode.insertBefore(select, inputUrl);
|
||||
|
||||
// Adicionar evento para desabilitar/limpar o input de URL quando um clone é selecionado
|
||||
select.addEventListener('change', () => {
|
||||
if (select.value) {
|
||||
inputUrl.value = '';
|
||||
inputUrl.placeholder = 'Site selecionado acima. (Ou limpe o select para usar URL nova)';
|
||||
inputUrl.disabled = true;
|
||||
agentsState.designer.currentClone = {
|
||||
directory: 'cloned-sites/' + select.value,
|
||||
name: select.value.split('_')[0],
|
||||
url: 'https://' + select.value.split('_')[0]
|
||||
};
|
||||
} else {
|
||||
inputUrl.disabled = false;
|
||||
inputUrl.placeholder = 'https://exemplo.com (Para clonar novo site)';
|
||||
agentsState.designer.currentClone = null;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Erro ao carregar lista de clones no Designer:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function startDesign() {
|
||||
const clone = agentsState.designer.currentClone;
|
||||
if (!clone) {
|
||||
// Try to get from URL input
|
||||
const url = document.getElementById('designUrl')?.value?.trim();
|
||||
const selectEl = document.getElementById('designerCloneSelect');
|
||||
const urlEl = document.getElementById('designUrl');
|
||||
|
||||
// Se escolheu na droplist
|
||||
if (selectEl && selectEl.value) {
|
||||
agentsState.designer.currentClone = {
|
||||
directory: 'cloned-sites/' + selectEl.value,
|
||||
name: selectEl.value.split('_')[0],
|
||||
url: 'https://' + selectEl.value.split('_')[0]
|
||||
};
|
||||
} else {
|
||||
const url = urlEl?.value?.trim();
|
||||
if (!url) {
|
||||
showAgentError('Designer', 'Nenhum site selecionado. Use o Scout ou insira uma URL.');
|
||||
showAgentError('Designer', 'Selecione um site já clonado na lista ou insira uma URL válida.');
|
||||
return;
|
||||
}
|
||||
agentsState.designer.currentClone = { url, name: 'Site selecionado' };
|
||||
@@ -181,22 +251,29 @@ async function startDesign() {
|
||||
updateDesignerStatus('🚀 Iniciando redesign ultra-moderno...');
|
||||
|
||||
try {
|
||||
// Step 1: Clone the site
|
||||
updateDesignerStatus('📥 Clonando site original...');
|
||||
const cloneResult = await cloneSite(agentsState.designer.currentClone.url);
|
||||
let directory = agentsState.designer.currentClone.directory;
|
||||
|
||||
if (!cloneResult.success) {
|
||||
throw new Error(cloneResult.error || 'Falha ao clonar');
|
||||
// Se NÃO escolheu na droplist (ou seja, se passou URL nova), faz o clone do zero!
|
||||
if (!directory) {
|
||||
updateDesignerStatus('📥 Clonando site original...');
|
||||
const cloneResult = await cloneSite(agentsState.designer.currentClone.url);
|
||||
|
||||
if (!cloneResult.success) {
|
||||
throw new Error(cloneResult.error || 'Falha ao clonar');
|
||||
}
|
||||
|
||||
directory = cloneResult.data?.directory || cloneResult.data?.path || cloneResult.directory;
|
||||
agentsState.designer.currentClone.directory = directory;
|
||||
updateDesignerStatus('✅ Site clonado com sucesso');
|
||||
} else {
|
||||
updateDesignerStatus('📂 Usando site já clonado do storage...');
|
||||
}
|
||||
|
||||
agentsState.designer.currentClone.directory = cloneResult.directory;
|
||||
updateDesignerStatus('✅ Site clonado com sucesso');
|
||||
|
||||
// Step 2: Apply ultra-modern redesign
|
||||
updateDesignerStatus('🎨 Aplicando redesign ultra-moderno...');
|
||||
const redesignResult = await applyUltraModernRedesign(cloneResult.directory);
|
||||
updateDesignerStatus('🎨 Aplicando redesign ultra-moderno com IA...');
|
||||
const redesignResult = await applyUltraModernRedesign(directory);
|
||||
|
||||
updateDesignerStatus('✨ Redesign concluído!');
|
||||
updateDesignerStatus('✨ Redesign concluído com sucesso!');
|
||||
showDesignerPreview(redesignResult.previewUrl);
|
||||
|
||||
} catch (err) {
|
||||
@@ -208,7 +285,7 @@ async function startDesign() {
|
||||
}
|
||||
|
||||
async function cloneSite(url) {
|
||||
const response = await fetch('/api/wget-clone', {
|
||||
const response = await fetch('/api/smart-clone', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ url })
|
||||
@@ -217,16 +294,54 @@ async function cloneSite(url) {
|
||||
}
|
||||
|
||||
async function applyUltraModernRedesign(cloneDir) {
|
||||
// Simula o redesign - em produção chamaria bsw-designer agent
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
const prompt = `Você é um Engenheiro de Frontend Especialista (Agente Designer), focado em aplicar designs Ultra-Modernos (baseados nas skills clone-website e performance).
|
||||
Retorne APENAS um objeto JSON simulando as alterações que você faria, com a estrutura:
|
||||
{"appliedStyles": ["estilo 1", "estilo 2"], "elementsRedesigned": ["elemento 1", "elemento 2"]}`;
|
||||
|
||||
const previewUrl = `http://localhost:${window.location.port || 3000}/${cloneDir.split('/').pop()}/index.html`;
|
||||
let appliedStyles = ['glassmorphism', 'gradient-bg', 'smooth-animations', 'dark-mode'];
|
||||
let elementsRedesigned = ['hero', 'cards', 'buttons', 'navigation', 'footer'];
|
||||
|
||||
try {
|
||||
const rawResponse = await askAI(prompt, `Planeje o redesign do site no diretório: ${cloneDir}`);
|
||||
let jsonStr = rawResponse.match(/\{[\s\S]*\}/)?.[0] || '{}';
|
||||
const parsed = JSON.parse(jsonStr);
|
||||
if (parsed.appliedStyles) appliedStyles = parsed.appliedStyles;
|
||||
if (parsed.elementsRedesigned) elementsRedesigned = parsed.elementsRedesigned;
|
||||
} catch (e) {
|
||||
console.error("Erro na IA do Designer:", e);
|
||||
}
|
||||
|
||||
// Remover barra no final se houver
|
||||
let cleanDir = cloneDir.endsWith('/') ? cloneDir.slice(0, -1) : cloneDir;
|
||||
let newPreviewUrl = `/api/preview/${cleanDir.split('/').pop()}`;
|
||||
try {
|
||||
const response = await fetch('/api/ai/inject-redesign', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
cloneDir,
|
||||
appliedStyles,
|
||||
elementsRedesigned,
|
||||
outputDir: settings.outputDir,
|
||||
agentName: 'designer'
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.newFolderName) {
|
||||
newPreviewUrl = `/api/preview/${data.newFolderName}`;
|
||||
if (agentsState.designer.currentClone) {
|
||||
agentsState.designer.currentClone.directory = 'cloned-sites/' + data.newFolderName;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Erro ao injetar CSS de redesign:", e);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
previewUrl,
|
||||
appliedStyles: ['glassmorphism', 'gradient-bg', 'smooth-animations', 'dark-mode'],
|
||||
elementsRedesigned: ['hero', 'cards', 'buttons', 'navigation', 'footer']
|
||||
previewUrl: newPreviewUrl,
|
||||
appliedStyles,
|
||||
elementsRedesigned
|
||||
};
|
||||
}
|
||||
|
||||
@@ -277,10 +392,9 @@ function updateDesignerStatus(status) {
|
||||
|
||||
async function sendOutreachMessage() {
|
||||
const targetId = document.getElementById('outreachTargetId')?.value;
|
||||
const message = document.getElementById('outreachMessage')?.value?.trim();
|
||||
|
||||
if (!targetId || !message) {
|
||||
showAgentError('Outreach', 'Selecione um alvo e escreva a mensagem');
|
||||
if (!targetId) {
|
||||
showAgentError('Outreach', 'Selecione um alvo');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -290,20 +404,29 @@ async function sendOutreachMessage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Simula envio - em produção usaria API de email/whatsapp
|
||||
await new Promise(r => setTimeout(r, 800));
|
||||
// Gerar mensagem via IA
|
||||
const prompt = `Você é um Copywriter (Agente Outreach). Escreva uma mensagem de vendas persuasiva para o cliente '${target.name}' cujo site '${target.url}' tem o problema: '${target.reason}'. Ofereça um redesign ultra-moderno. Responda apenas o texto da mensagem (sem formatação json ou markdown).`;
|
||||
|
||||
agentsState.outreach.messages.push({
|
||||
to: target.name,
|
||||
toUrl: target.url,
|
||||
message,
|
||||
sentAt: new Date().toISOString(),
|
||||
status: 'sent'
|
||||
});
|
||||
try {
|
||||
const message = await askAI(prompt, "Crie o e-mail de vendas.");
|
||||
|
||||
agentsState.outreach.sent++;
|
||||
updateOutreachStats();
|
||||
showOutreachConfirmation(target.name);
|
||||
const msgBox = document.getElementById('outreachMessage');
|
||||
if (msgBox) msgBox.value = message;
|
||||
|
||||
agentsState.outreach.messages.push({
|
||||
to: target.name,
|
||||
toUrl: target.url,
|
||||
message,
|
||||
sentAt: new Date().toISOString(),
|
||||
status: 'sent'
|
||||
});
|
||||
|
||||
agentsState.outreach.sent++;
|
||||
updateOutreachStats();
|
||||
showOutreachConfirmation(target.name);
|
||||
} catch(e) {
|
||||
showAgentError('Outreach', "Falha ao gerar mensagem: " + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function showOutreachConfirmation(targetName) {
|
||||
@@ -336,29 +459,72 @@ async function runOrchestratorPipeline() {
|
||||
}
|
||||
|
||||
setOrchestratorRunning(true);
|
||||
updateOrchestratorStatus('🎬 Iniciando pipeline completo...');
|
||||
updateOrchestratorStatus('🎬 Iniciando pipeline completo com IA (Orchestrator)...');
|
||||
|
||||
agentsState.orchestrator.pipeline = targets;
|
||||
agentsState.orchestrator.currentStep = 0;
|
||||
|
||||
for (let i = 0; i < targets.length; i++) {
|
||||
const target = targets[i];
|
||||
updateOrchestratorStatus(`📋 [${i+1}/${targets.length}] Processando: ${target.name}`);
|
||||
updateOrchestratorStatus(`📋 [${i+1}/${targets.length}] Planejando com IA: ${target.name}`);
|
||||
|
||||
// Orchestrator AI Prompt integrating the 3 skills
|
||||
const orchPrompt = `Você é o Agente Orchestrator mestre do WebClone. Baseado nas skills 'firecrawl-website-design-clone', 'clone-website' e 'performance', analise o alvo '${target.name}' (${target.url}) que possui o problema '${target.reason}'. Defina uma estratégia de clonagem e otimização em uma única frase concisa.`;
|
||||
|
||||
try {
|
||||
const strategy = await askAI(orchPrompt, "Defina a estratégia de clonagem.");
|
||||
console.log(`[Orchestrator Strategy - ${target.name}]:`, strategy);
|
||||
target.orchestratorStrategy = strategy;
|
||||
} catch (e) {
|
||||
console.error("Erro na IA do Orchestrator:", e);
|
||||
target.orchestratorStrategy = "Estratégia padrão de clonagem e otimização de performance.";
|
||||
}
|
||||
|
||||
// Step 1: Clone
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
updateOrchestratorStatus(`📥 [${i+1}/${targets.length}] Clonando: ${target.name}`);
|
||||
let directory = `cloned-sites/${target.name.toLowerCase().replace(/[^a-z0-9]/g, '')}`;
|
||||
try {
|
||||
const cloneRes = await cloneSite(target.url);
|
||||
if (cloneRes.success && (cloneRes.data?.directory || cloneRes.data?.path || cloneRes.directory)) {
|
||||
directory = cloneRes.data?.directory || cloneRes.data?.path || cloneRes.directory;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Aviso ao clonar ${target.name}, usando diretório de fallback:`, e.message);
|
||||
}
|
||||
target.directory = directory;
|
||||
|
||||
// Step 2: Redesign
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
updateOrchestratorStatus(`🎨 [${i+1}/${targets.length}] Aplicando Redesign AI: ${target.name}`);
|
||||
try {
|
||||
const redesignRes = await applyUltraModernRedesign(directory);
|
||||
target.redesign = redesignRes;
|
||||
} catch (e) {
|
||||
console.error(`Erro no redesign de ${target.name}:`, e);
|
||||
}
|
||||
|
||||
// Step 3: Save to history
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
// Step 3: Outreach AI generation
|
||||
updateOrchestratorStatus(`✉️ [${i+1}/${targets.length}] Gerando Copy de Outreach: ${target.name}`);
|
||||
const outreachPrompt = `Você é um Copywriter (Agente Outreach). Escreva uma mensagem de vendas persuasiva para '${target.name}' (${target.url}) focando em resolver '${target.reason}' com um redesign ultra-moderno e alta performance. Responda apenas o texto do e-mail.`;
|
||||
try {
|
||||
const message = await askAI(outreachPrompt, "Crie o e-mail de vendas.");
|
||||
agentsState.outreach.messages.push({
|
||||
to: target.name,
|
||||
toUrl: target.url,
|
||||
message,
|
||||
sentAt: new Date().toISOString(),
|
||||
status: 'ready'
|
||||
});
|
||||
agentsState.outreach.sent++;
|
||||
updateOutreachStats();
|
||||
} catch (e) {
|
||||
console.error(`Erro no outreach de ${target.name}:`, e);
|
||||
}
|
||||
|
||||
agentsState.orchestrator.currentStep = i + 1;
|
||||
updateOrchestratorProgress((i + 1) / targets.length * 100);
|
||||
}
|
||||
|
||||
updateOrchestratorStatus(`✅ Pipeline completo! ${targets.length} sites processados.`);
|
||||
updateOrchestratorStatus(`✅ Pipeline completo! ${targets.length} sites processados com IA.`);
|
||||
setOrchestratorRunning(false);
|
||||
}
|
||||
|
||||
@@ -437,5 +603,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
btn.addEventListener('click', () => switchAgentTab(btn.dataset.tab));
|
||||
});
|
||||
|
||||
// Carregar a droplist de clones do Designer
|
||||
loadDesignerClonesSelect();
|
||||
|
||||
console.log('✅ BrainSteel Web Agents initialized');
|
||||
});
|
||||
|
||||
+102
-23
@@ -59,7 +59,8 @@ function navigateToPage(page) {
|
||||
'dashboard': 'Dashboard',
|
||||
'new-clone': 'Nova Clonagem',
|
||||
'history': 'Histórico',
|
||||
'settings': 'Configurações'
|
||||
'settings': 'Configurações',
|
||||
'optimized-pages': 'Páginas Otimizadas (AI)'
|
||||
};
|
||||
document.getElementById('pageTitle').textContent = titles[page] || page;
|
||||
|
||||
@@ -70,6 +71,8 @@ function navigateToPage(page) {
|
||||
loadDashboard();
|
||||
} else if (page === 'history') {
|
||||
loadHistory();
|
||||
} else if (page === 'optimized-pages') {
|
||||
loadOptimizedPages();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +209,7 @@ async function handleCloneSubmit(e) {
|
||||
const includeCSSEl = document.getElementById('includeCSS');
|
||||
const includeJSEl = document.getElementById('includeJS');
|
||||
const convertLinksEl = document.getElementById('convertLinks');
|
||||
const customFolderEl = document.getElementById('customFolder');
|
||||
|
||||
if (!cloneUrlEl || !depthLevelEl || !includeImagesEl || !includeCSSEl || !includeJSEl || !convertLinksEl) {
|
||||
console.error('Form elements not found');
|
||||
@@ -219,6 +223,14 @@ async function handleCloneSubmit(e) {
|
||||
const includeCSS = includeCSSEl.checked;
|
||||
const includeJS = includeJSEl.checked;
|
||||
const convertLinks = convertLinksEl.checked;
|
||||
let folderName = customFolderEl ? customFolderEl.value.trim() : '';
|
||||
if (folderName.includes('/root/Desktop/CLONE_SITE/')) {
|
||||
folderName = folderName.replace('/root/Desktop/CLONE_SITE/', '').trim();
|
||||
} else if (folderName.includes('/root/Desktop/CLONE_SITE')) {
|
||||
folderName = folderName.replace('/root/Desktop/CLONE_SITE', '').trim();
|
||||
}
|
||||
// Caso tenha digitado caminhos absolutos, extrair apenas o final
|
||||
folderName = folderName.replace(/[\/\\]/g, '').trim();
|
||||
const cloneMethod = 'wget';
|
||||
|
||||
if (!url) {
|
||||
@@ -240,6 +252,7 @@ async function handleCloneSubmit(e) {
|
||||
includeCSS,
|
||||
includeJS,
|
||||
convertLinks,
|
||||
folderName,
|
||||
method: cloneMethod
|
||||
});
|
||||
}
|
||||
@@ -304,7 +317,11 @@ async function startCloning(url, options) {
|
||||
response = await fetch('/api/smart-clone', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ url })
|
||||
body: JSON.stringify({
|
||||
url,
|
||||
outputDir: settings.outputDir,
|
||||
folderName: options.folderName
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// WGET logic
|
||||
@@ -313,7 +330,9 @@ async function startCloning(url, options) {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
url,
|
||||
depth: options.depth
|
||||
depth: options.depth,
|
||||
outputDir: settings.outputDir,
|
||||
folderName: options.folderName
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -329,9 +348,8 @@ async function startCloning(url, options) {
|
||||
progressMessage.textContent = '✅ Clonagem concluída com sucesso!';
|
||||
|
||||
// Update stats
|
||||
// Update stats
|
||||
const files = data.data.resources || data.data.files || 0;
|
||||
const size = data.data.size || 0; // Smart clone might not return size yet
|
||||
const files = data.data?.files || data.data?.cloneInfo?.fileCount || data.data?.resources || data.files || 0;
|
||||
const size = data.data?.size || data.data?.cloneInfo?.totalSize || data.size || 0;
|
||||
|
||||
filesDownloaded.textContent = files;
|
||||
sizeDownloaded.textContent = formatBytes(size);
|
||||
@@ -585,35 +603,38 @@ function loadSettings() {
|
||||
}
|
||||
|
||||
// Populate settings form (if needed)
|
||||
const outDirInput = document.getElementById('settingsOutputDir');
|
||||
if (outDirInput) outDirInput.value = settings.outputDir || '/root/Desktop/CLONE_SITE';
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
const outDirInput = document.getElementById('settingsOutputDir');
|
||||
if (outDirInput) settings.outputDir = outDirInput.value;
|
||||
localStorage.setItem('cloneSettings', JSON.stringify(settings));
|
||||
showToast('Configurações salvas!', 'success');
|
||||
}
|
||||
|
||||
// Utility Functions
|
||||
function openClonedSite(directory) {
|
||||
// Try to open via server endpoint
|
||||
fetch('/api/health', {
|
||||
method: 'GET'
|
||||
}).then(response => {
|
||||
if (response.ok) {
|
||||
// Server is running, open the site via same origin
|
||||
const siteName = directory.split('/').pop();
|
||||
window.open(`/${siteName}/index.html`, '_blank');
|
||||
} else {
|
||||
showToast('Servidor não está respondendo.', 'error');
|
||||
}
|
||||
}).catch(error => {
|
||||
showToast('Servidor não está respondendo.', 'error');
|
||||
});
|
||||
const siteName = directory.replace(/\\/g, '/').split('/').pop();
|
||||
window.open(`/api/preview/${siteName}`, '_blank');
|
||||
}
|
||||
|
||||
function openCloneFolder(directory) {
|
||||
showToast('Abrindo pasta: ' + directory, 'info');
|
||||
// In a real app, this would use an API endpoint to open the folder
|
||||
console.log('Open folder:', directory);
|
||||
showToast('Abrindo gerenciador de arquivos web...', 'info');
|
||||
fetch('/api/open-folder', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: directory })
|
||||
}).then(res => res.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
showToast('Erro ao abrir pasta: ' + (data.error || ''), 'error');
|
||||
} else if (data.url) {
|
||||
window.open(data.url, '_blank');
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('Erro ao chamar open-folder:', err));
|
||||
}
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
@@ -703,3 +724,61 @@ function formatRelativeTime(dateString) {
|
||||
if (minutes > 0) return minutes === 1 ? 'Há 1 minuto' : `Há ${minutes} minutos`;
|
||||
return 'Agora mesmo';
|
||||
}
|
||||
|
||||
// Optimized Pages
|
||||
async function loadOptimizedPages() {
|
||||
const listEl = document.getElementById('optimizedList');
|
||||
if (!listEl) return;
|
||||
|
||||
listEl.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<h3>Carregando...</h3>
|
||||
</div>
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/optimized-pages');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.pages.length > 0) {
|
||||
listEl.innerHTML = data.pages.map(page => `
|
||||
<div class="clone-item">
|
||||
<div class="clone-info">
|
||||
<div class="clone-icon" style="background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);">
|
||||
<i class="fas fa-magic" style="color: white;"></i>
|
||||
</div>
|
||||
<div class="clone-details">
|
||||
<h4>${page.name}</h4>
|
||||
<p>Otimizada pela IA • ${page.directory}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clone-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="openClonedSite('${page.directory}')">
|
||||
<i class="fas fa-external-link-alt"></i> Visualizar
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="openCloneFolder('${page.directory}')">
|
||||
<i class="fas fa-folder-open"></i> Pasta
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
listEl.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-paint-brush"></i>
|
||||
<h3>Nenhuma página otimizada encontrada</h3>
|
||||
<p>Use o Agente Designer para otimizar suas clonagens.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} catch (e) {
|
||||
listEl.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<h3>Erro ao carregar</h3>
|
||||
<p>${e.message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,8 @@ urlInput.addEventListener('keypress', (e) => {
|
||||
|
||||
document.getElementById('btnOpenSite').addEventListener('click', () => {
|
||||
if (clonedData && clonedData.data && clonedData.data.path) {
|
||||
// Construct local URL. Server serves /<folderName>/index.html
|
||||
// We know server serves static files from cloned-sites directly if configured?
|
||||
// Wait, server.js serves ../cloned-sites as static.
|
||||
// so path is just /FolderName/index.html
|
||||
const dirName = clonedData.data.path.split('\\').pop().split('/').pop();
|
||||
window.open(`/${dirName}/index.html`, '_blank');
|
||||
window.open(`/api/preview/${dirName}`, '_blank');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,7 +26,12 @@ document.getElementById('btnOpenFolder').addEventListener('click', () => {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: clonedData.data.path })
|
||||
});
|
||||
}).then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.success && data.url) {
|
||||
window.open(data.url, '_blank');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+13
-23
@@ -255,7 +255,7 @@ function populateOverview(data) {
|
||||
if (data.cloneInfo) {
|
||||
overviewItems.push(
|
||||
{ label: 'Clone Status', value: data.cloneInfo.success ? '✅ Sucesso' : '❌ Falhou' },
|
||||
{ label: 'Assets Baixados', value: `${data.cloneInfo.assetsDownloaded}/${data.cloneInfo.totalAssets}` },
|
||||
{ label: 'Assets Baixados', value: data.cloneInfo.fileCount ? `${data.cloneInfo.fileCount} arquivos (${formatBytes(data.cloneInfo.totalSize || 0)})` : 'verificando...' },
|
||||
{ label: 'Diretório Local', value: data.cloneInfo.directory }
|
||||
);
|
||||
}
|
||||
@@ -418,35 +418,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Funções para gerenciar sites clonados
|
||||
function openCloneFolder(directory) {
|
||||
// Tentar abrir a pasta no explorador do Windows
|
||||
fetch('/api/open-folder', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ path: directory })
|
||||
}).then(response => {
|
||||
if (!response.ok) {
|
||||
alert('Não foi possível abrir a pasta automaticamente.\nLocalização: ' + directory);
|
||||
}
|
||||
}).catch(error => {
|
||||
alert('Não foi possível abrir a pasta automaticamente.\nLocalização: ' + directory);
|
||||
});
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.url) {
|
||||
window.open(data.url, '_blank');
|
||||
} else {
|
||||
alert('Não foi possível abrir a pasta.\nLocalização: ' + directory);
|
||||
}
|
||||
}).catch(error => {
|
||||
alert('Não foi possível abrir a pasta.\nLocalização: ' + directory);
|
||||
});
|
||||
}
|
||||
|
||||
function openClonedSite(directory) {
|
||||
// Tentar abrir o index.html do site clonado
|
||||
fetch('/api/open-site', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ path: directory })
|
||||
}).then(response => {
|
||||
if (!response.ok) {
|
||||
alert('Não foi possível abrir o site automaticamente.\nAbra manualmente: ' + directory + '\\index.html');
|
||||
}
|
||||
}).catch(error => {
|
||||
alert('Não foi possível abrir o site automaticamente.\nAbra manualmente: ' + directory + '\\index.html');
|
||||
});
|
||||
const folderName = directory.split('/').pop();
|
||||
window.open('/api/preview/' + folderName, '_blank');
|
||||
}
|
||||
@@ -40,6 +40,11 @@
|
||||
<span>Storage</span>
|
||||
</a>
|
||||
|
||||
<a href="#" class="nav-item" data-page="optimized-pages">
|
||||
<i class="fas fa-paint-brush"></i>
|
||||
<span>Otimizados (AI)</span>
|
||||
</a>
|
||||
|
||||
<a href="#" class="nav-item" data-page="settings">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>Configurações</span>
|
||||
@@ -150,6 +155,17 @@
|
||||
required
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="customFolder">
|
||||
<i class="fas fa-folder"></i> Nome Específico da Pasta (Opcional)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="customFolder"
|
||||
class="form-control"
|
||||
placeholder="ex: meu_projeto"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
@@ -238,6 +254,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Optimized Pages Page -->
|
||||
<div class="page" id="optimized-pages">
|
||||
<div class="section-header">
|
||||
<h3><i class="fas fa-paint-brush"></i> Gestão de Páginas Otimizadas pela IA</h3>
|
||||
<div class="header-actions">
|
||||
<button class="btn btn-secondary btn-sm" onclick="loadOptimizedPages()">
|
||||
<i class="fas fa-sync-alt"></i> Atualizar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clones-list" id="optimizedList">
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<h3>Carregando...</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Page -->
|
||||
<div class="page" id="settings">
|
||||
<div class="settings-container">
|
||||
@@ -250,8 +285,8 @@
|
||||
<p>Local onde os sites clonados serão salvos</p>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<input type="text" class="form-control" value="M:\CLONEWEB\cloned-sites" readonly>
|
||||
<button class="btn btn-secondary btn-sm">
|
||||
<input type="text" id="settingsOutputDir" class="form-control" value="/root/Desktop/Clonados">
|
||||
<button class="btn btn-secondary btn-sm" onclick="openCloneFolder(document.getElementById('settingsOutputDir').value)">
|
||||
<i class="fas fa-folder-open"></i> Abrir
|
||||
</button>
|
||||
</div>
|
||||
@@ -293,7 +328,7 @@
|
||||
</div>
|
||||
|
||||
<div class="settings-actions">
|
||||
<button class="btn btn-primary">
|
||||
<button class="btn btn-primary" onclick="saveSettings()">
|
||||
<i class="fas fa-save"></i> Salvar Configurações
|
||||
</button>
|
||||
<button class="btn btn-secondary">
|
||||
@@ -386,7 +421,8 @@
|
||||
|
||||
<div class="agent-inputs">
|
||||
<input type="text" id="designUrl" class="form-control" placeholder="URL do site para redesign...">
|
||||
<button id="startDesignBtn" class="btn btn-primary">
|
||||
|
||||
<button id="startDesignBtn" class="btn btn-primary mt-3">
|
||||
<i class="fas fa-rocket"></i> Iniciar Redesign
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -373,7 +373,7 @@ function confirmDeleteClone(cloneId) {
|
||||
}
|
||||
|
||||
function openClone(cloneId) {
|
||||
window.open(`/${cloneId}/index.html`, '_blank');
|
||||
window.open(`/api/preview/${cloneId}`, '_blank');
|
||||
}
|
||||
|
||||
function cloneSiteAgain(baseUrl) {
|
||||
|
||||
+587
-29
@@ -3,10 +3,18 @@ const path = require('path');
|
||||
const cors = require('cors');
|
||||
const axios = require('axios');
|
||||
const { exec, spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
// Security / Iframe Fix
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
res.removeHeader('X-Frame-Options'); // Try to remove it completely first, if proxy allows
|
||||
next();
|
||||
});
|
||||
|
||||
// Storage manager routes
|
||||
const storageManager = require('./storage-manager');
|
||||
app.use('/api/clones', storageManager);
|
||||
@@ -24,6 +32,93 @@ app.use((req, res, next) => {
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// Helper para parsear cookies
|
||||
function parseCookies(cookieHeader) {
|
||||
const list = {};
|
||||
if (!cookieHeader) return list;
|
||||
cookieHeader.split(';').forEach(cookie => {
|
||||
let [name, ...rest] = cookie.split('=');
|
||||
name = name?.trim();
|
||||
if (!name) return;
|
||||
const value = rest.join('=').trim();
|
||||
if (!value) return;
|
||||
list[name] = decodeURIComponent(value);
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
// Rota para ativar o Virtual Root Preview (define o cookie e redireciona para a raiz)
|
||||
app.get('/api/preview/:folder', (req, res) => {
|
||||
const folderName = req.params.folder;
|
||||
res.setHeader('Set-Cookie', `clone_preview_target=${encodeURIComponent(folderName)}; Path=/; HttpOnly; SameSite=Lax`);
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
// Rota para sair do Virtual Root Preview (limpa o cookie e volta ao dashboard)
|
||||
app.get('/api/exit-preview', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'clone_preview_target=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
// Middleware de Virtual Root Proxy (serve o site clonado diretamente na raiz / quando o cookie está ativo)
|
||||
app.use((req, res, next) => {
|
||||
const cookies = parseCookies(req.headers.cookie);
|
||||
if (cookies.clone_preview_target && !req.path.startsWith('/api') && req.path !== '/old') {
|
||||
const folderName = cookies.clone_preview_target;
|
||||
const folderPath = path.join(__dirname, '..', 'cloned-sites', folderName);
|
||||
|
||||
if (fs.existsSync(folderPath)) {
|
||||
let targetFile = req.path;
|
||||
if (targetFile === '/') targetFile = '/index.html';
|
||||
|
||||
// Se o arquivo solicitado existir dentro da pasta do clone
|
||||
const fullPath = path.join(folderPath, targetFile);
|
||||
|
||||
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
||||
// Se for HTML, injeta a barra de preview flutuante
|
||||
if (targetFile.endsWith('.html')) {
|
||||
let html = fs.readFileSync(fullPath, 'utf8');
|
||||
const previewBar = `
|
||||
<div id="__cloneweb_preview_bar" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #1f2937; color: white; padding: 12px 24px; border-radius: 50px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); display: flex; align-items: center; gap: 16px; z-index: 2147483647; font-family: 'Inter', sans-serif; font-size: 14px;">
|
||||
<span style="display: flex; align-items: center; gap: 8px;"><i class="fas fa-eye" style="color: #10b981;"></i> Live Preview: <strong style="color: #60a5fa;">${folderName.split('_')[0]}</strong></span>
|
||||
<a href="/api/exit-preview" style="background: #ef4444; color: white; padding: 6px 16px; border-radius: 30px; text-decoration: none; font-weight: 600; transition: all 0.2s; box-shadow: 0 4px 12px rgba(239,68,68,0.3); font-size: 13px;">Sair do Preview</a>
|
||||
</div>
|
||||
`;
|
||||
if (html.includes('</body>')) {
|
||||
html = html.replace('</body>', previewBar + '</body>');
|
||||
} else {
|
||||
html += previewBar;
|
||||
}
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
return res.send(html);
|
||||
}
|
||||
|
||||
// Para outros arquivos (CSS, JS, imagens), serve diretamente com o MIME type correto
|
||||
if (targetFile.endsWith('.css')) res.setHeader('Content-Type', 'text/css');
|
||||
if (targetFile.endsWith('.js')) res.setHeader('Content-Type', 'application/javascript');
|
||||
return res.sendFile(fullPath);
|
||||
} else {
|
||||
// Fallback de SPA: se a rota virtual não existir como arquivo no disco, serve o index.html do clone!!!
|
||||
const indexPath = path.join(folderPath, 'index.html');
|
||||
if (fs.existsSync(indexPath)) {
|
||||
let html = fs.readFileSync(indexPath, 'utf8');
|
||||
const previewBar = `
|
||||
<div id="__cloneweb_preview_bar" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #1f2937; color: white; padding: 12px 24px; border-radius: 50px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); display: flex; align-items: center; gap: 16px; z-index: 2147483647; font-family: 'Inter', sans-serif; font-size: 14px;">
|
||||
<span style="display: flex; align-items: center; gap: 8px;"><i class="fas fa-eye" style="color: #10b981;"></i> Live Preview: <strong style="color: #60a5fa;">${folderName.split('_')[0]}</strong></span>
|
||||
<a href="/api/exit-preview" style="background: #ef4444; color: white; padding: 6px 16px; border-radius: 30px; text-decoration: none; font-weight: 600; transition: all 0.2s; box-shadow: 0 4px 12px rgba(239,68,68,0.3); font-size: 13px;">Sair do Preview</a>
|
||||
</div>
|
||||
`;
|
||||
if (html.includes('</body>')) html = html.replace('</body>', previewBar + '</body>');
|
||||
else html += previewBar;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
return res.send(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Serve static files with proper MIME types
|
||||
app.use(express.static(path.join(__dirname, 'public'), {
|
||||
setHeaders: (res, path) => {
|
||||
@@ -33,6 +128,137 @@ app.use(express.static(path.join(__dirname, 'public'), {
|
||||
}
|
||||
}));
|
||||
|
||||
// Middleware para injetar fix de roteador em sites clonados (SPAs como React, Vue, Angular, Vite) e reescrever JS
|
||||
app.use((req, res, next) => {
|
||||
if (!req.path.startsWith('/api') && req.path !== '/' && req.path !== '/old') {
|
||||
let urlPath = req.path;
|
||||
// Remover prefixo /cloned-sites se existir para buscar corretamente no disco
|
||||
if (urlPath.startsWith('/cloned-sites/')) {
|
||||
urlPath = urlPath.replace('/cloned-sites/', '/');
|
||||
}
|
||||
|
||||
if (!urlPath.match(/\.[a-zA-Z0-9]+$/)) {
|
||||
if (!urlPath.endsWith('/')) urlPath += '/';
|
||||
urlPath += 'index.html';
|
||||
}
|
||||
|
||||
// Interceptação de arquivos HTML (Camada 1 - DOM Mock Universal)
|
||||
if (urlPath.endsWith('.html')) {
|
||||
const filePath = path.join(__dirname, '..', 'cloned-sites', urlPath);
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
let html = fs.readFileSync(filePath, 'utf8');
|
||||
if (!html.includes('__CLONEWEB_ROUTER_FIX__')) {
|
||||
const injectScript = `
|
||||
<script id="__CLONEWEB_ROUTER_FIX__">
|
||||
(function() {
|
||||
try {
|
||||
window.__mockLocationPathname = '/';
|
||||
window.__mockLocationHref = window.location.protocol + '//' + window.location.host + '/';
|
||||
|
||||
let targetObj = window.location;
|
||||
let proto = Object.getPrototypeOf(targetObj);
|
||||
let pathDesc = null;
|
||||
let hrefDesc = null;
|
||||
|
||||
while (proto && (!pathDesc || !hrefDesc)) {
|
||||
if (!pathDesc) pathDesc = Object.getOwnPropertyDescriptor(proto, 'pathname');
|
||||
if (!hrefDesc) hrefDesc = Object.getOwnPropertyDescriptor(proto, 'href');
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
if (!pathDesc) pathDesc = Object.getOwnPropertyDescriptor(targetObj, 'pathname');
|
||||
if (!hrefDesc) hrefDesc = Object.getOwnPropertyDescriptor(targetObj, 'href');
|
||||
|
||||
if (pathDesc && pathDesc.configurable) {
|
||||
const origGet = pathDesc.get;
|
||||
Object.defineProperty(proto || targetObj, 'pathname', {
|
||||
get: function() {
|
||||
const p = origGet ? origGet.call(this) : this.pathname;
|
||||
if (p !== '/' && p !== '/index.html' && (p.includes('_202') || p.includes('/cloned-sites/'))) {
|
||||
return '/';
|
||||
}
|
||||
return p;
|
||||
},
|
||||
set: pathDesc.set || function(v) { window.location.assign(v); }
|
||||
});
|
||||
}
|
||||
|
||||
if (hrefDesc && hrefDesc.configurable) {
|
||||
const origHrefGet = hrefDesc.get;
|
||||
Object.defineProperty(proto || targetObj, 'href', {
|
||||
get: function() {
|
||||
const h = origHrefGet ? origHrefGet.call(this) : this.href;
|
||||
if (h.includes('_202') || h.includes('/cloned-sites/')) {
|
||||
const u = new URL(h);
|
||||
return u.protocol + '//' + u.host + '/';
|
||||
}
|
||||
return h;
|
||||
},
|
||||
set: hrefDesc.set || function(v) { window.location.assign(v); }
|
||||
});
|
||||
}
|
||||
|
||||
const OriginalURL = window.URL;
|
||||
window.URL = function(url, base) {
|
||||
if (typeof url === 'string' && (url.includes('_202') || url.includes('/cloned-sites/'))) {
|
||||
url = url.replace(/\\/cloned-sites\\/[^/]+/gi, '').replace(/\\/[^/]+_202[0-9-_T:.]+(\\/index\\.html)?/gi, '/');
|
||||
}
|
||||
return new OriginalURL(url, base);
|
||||
};
|
||||
window.URL.prototype = OriginalURL.prototype;
|
||||
Object.assign(window.URL, OriginalURL);
|
||||
|
||||
console.log('🚀 [CloneWeb] Ultimate SPA Router fix initialized successfully (Cross-browser Gecko/Blink support).');
|
||||
} catch(e) {
|
||||
console.error('⚠️ [CloneWeb] DOM mock fallback active. Using __mockLocationPathname. Error:', e);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
`;
|
||||
if (html.includes('<head>')) {
|
||||
html = html.replace('<head>', '<head>' + injectScript);
|
||||
} else {
|
||||
html = injectScript + html;
|
||||
}
|
||||
}
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
return res.send(html);
|
||||
} catch (err) {
|
||||
console.error('Erro ao injetar fix no HTML do clone:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interceptação de arquivos JS de sites clonados (Camada 2 - JS Rewriter Supremo)
|
||||
if (urlPath.endsWith('.js') && (urlPath.includes('_202') || urlPath.includes('/cloned-sites/'))) {
|
||||
const filePath = path.join(__dirname, '..', 'cloned-sites', urlPath);
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
let jsContent = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// Substitui window.location.pathname de forma segura
|
||||
jsContent = jsContent.replace(/\bwindow\.location\.pathname\b/g, '(window.__mockLocationPathname || window.location.pathname)');
|
||||
|
||||
// Substitui location.pathname isolado (evita quebrar e.location.pathname ou history.location.pathname)
|
||||
jsContent = jsContent.replace(/(?<!\w\.)\blocation\.pathname\b/g, '(window.__mockLocationPathname || window.location.pathname)');
|
||||
|
||||
// Substitui window.location.href de forma segura
|
||||
jsContent = jsContent.replace(/\bwindow\.location\.href\b/g, '(window.__mockLocationHref || window.location.href)');
|
||||
|
||||
// Substitui location.href isolado (evita quebrar e.location.href ou history.location.href)
|
||||
jsContent = jsContent.replace(/(?<!\w\.)\blocation\.href\b/g, '(window.__mockLocationHref || window.location.href)');
|
||||
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
return res.send(jsContent);
|
||||
} catch (err) {
|
||||
console.error('Erro ao reescrever JS do clone:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Serve cloned sites directory
|
||||
app.use(express.static(path.join(__dirname, '..', 'cloned-sites'), {
|
||||
setHeaders: (res, filePath) => {
|
||||
@@ -77,14 +303,15 @@ app.post('/api/wget-clone', async (req, res) => {
|
||||
console.log('✅ Wget clone completed successfully');
|
||||
|
||||
// Sync to host /root/Desktop/Clonados
|
||||
if (response.data && response.data.success && response.data.directory) {
|
||||
const directory = response.data.data?.directory || response.data.directory;
|
||||
if (response.data && response.data.success && directory) {
|
||||
const { exec } = require('child_process');
|
||||
const containerId = require('os').hostname();
|
||||
const cloneDir = response.data.directory.split('/').pop();
|
||||
const hostDir = '/root/Desktop/Clonados';
|
||||
exec(`docker cp b38l2zsxjv08xkxg4gd5rl9r-210808689309:${response.data.directory} ${hostDir}/ 2>/dev/null`, (err) => {
|
||||
const cloneDir = directory.split('/').pop();
|
||||
const hostDir = req.body.outputDir || '/root/Desktop/CLONE_SITE';
|
||||
exec(`mkdir -p "${hostDir}" && cp -r "${directory}" "${hostDir}/" 2>/dev/null || docker cp ${containerId}:"${directory}" "${hostDir}/" 2>/dev/null`, (err) => {
|
||||
if (err) console.log('⚠️ Sync to host failed:', err.message);
|
||||
else console.log('✅ Clone synced to host:', cloneDir);
|
||||
else console.log('✅ Clone synced to host Desktop:', cloneDir);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,7 +327,7 @@ app.post('/api/wget-clone', async (req, res) => {
|
||||
|
||||
// Smart Clone Endpoint (Local Spawn)
|
||||
app.post('/api/smart-clone', (req, res) => {
|
||||
const { url } = req.body;
|
||||
const { url, folderName } = req.body;
|
||||
|
||||
if (!url) {
|
||||
return res.status(400).json({ error: 'URL is required' });
|
||||
@@ -111,9 +338,12 @@ app.post('/api/smart-clone', (req, res) => {
|
||||
|
||||
// Spawn the smart cloner process
|
||||
const scriptPath = path.join(__dirname, '..', 'simple-crawler', 'smart-cloner.js');
|
||||
console.log(`[SmartClone] Executing: node "${scriptPath}" --url="${url}"`);
|
||||
let spawnArgs = [scriptPath, `--url=${url}`];
|
||||
if (folderName) spawnArgs.push(`--folderName=${folderName}`);
|
||||
|
||||
const child = spawn('node', [scriptPath, `--url=${url}`], {
|
||||
console.log(`[SmartClone] Executing: node "${scriptPath}" --url="${url}" ${folderName ? `--folderName="${folderName}"` : ''}`);
|
||||
|
||||
const child = spawn('node', spawnArgs, {
|
||||
cwd: path.join(__dirname, '..'),
|
||||
shell: true
|
||||
});
|
||||
@@ -151,6 +381,20 @@ app.post('/api/smart-clone', (req, res) => {
|
||||
if (matches && matches.length > 0) {
|
||||
const lastMatch = matches[matches.length - 1];
|
||||
const result = JSON.parse(lastMatch);
|
||||
|
||||
// Sync to host /root/Desktop/Clonados
|
||||
const directory = result.data?.directory || result.directory || result.path;
|
||||
if (result.success && directory) {
|
||||
const { exec } = require('child_process');
|
||||
const containerId = require('os').hostname();
|
||||
const cloneDir = directory.split('/').pop();
|
||||
const hostDir = req.body.outputDir || '/root/Desktop/CLONE_SITE';
|
||||
exec(`mkdir -p "${hostDir}" && cp -r "${directory}" "${hostDir}/" 2>/dev/null || docker cp ${containerId}:"${directory}" "${hostDir}/" 2>/dev/null`, (err) => {
|
||||
if (err) console.log('⚠️ SmartClone sync to host failed:', err.message);
|
||||
else console.log('✅ SmartClone synced to host Desktop:', cloneDir);
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: result.success,
|
||||
data: result,
|
||||
@@ -185,7 +429,102 @@ app.get('/api/health', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Abrir pasta do clone
|
||||
// Rota de visualização de arquivos da pasta clonada (Gerenciador de Arquivos Web Embutido)
|
||||
app.get('/api/browse/:folder', (req, res) => {
|
||||
try {
|
||||
const folderName = req.params.folder;
|
||||
const folderPath = path.join(__dirname, '..', 'cloned-sites', folderName);
|
||||
|
||||
if (!fs.existsSync(folderPath)) {
|
||||
return res.status(404).send('<h1>Pasta não encontrada</h1><p>A pasta solicitada não existe no servidor.</p>');
|
||||
}
|
||||
|
||||
const items = fs.readdirSync(folderPath);
|
||||
let filesHtml = '';
|
||||
|
||||
items.forEach(item => {
|
||||
const itemPath = path.join(folderPath, item);
|
||||
const stat = fs.statSync(itemPath);
|
||||
const isDir = stat.isDirectory();
|
||||
const size = isDir ? '-' : (stat.size / 1024).toFixed(2) + ' KB';
|
||||
const date = new Date(stat.mtime).toLocaleString('pt-BR');
|
||||
const icon = isDir ? 'fa-folder' : (item.endsWith('.html') ? 'fa-file-code' : (item.endsWith('.json') ? 'fa-file-alt' : 'fa-file'));
|
||||
const link = isDir ? `#` : `/${folderName}/${item}`;
|
||||
|
||||
filesHtml += `
|
||||
<tr>
|
||||
<td><i class="fas ${icon}" style="color: #4f46e5; margin-right: 10px;"></i> <a href="${link}" target="_blank" style="color: #1f2937; text-decoration: none; font-weight: 500;">${item}</a></td>
|
||||
<td style="color: #6b7280;">${size}</td>
|
||||
<td style="color: #6b7280;">${date}</td>
|
||||
<td>
|
||||
${!isDir ? `<a href="${link}" target="_blank" class="btn btn-secondary btn-sm" style="background: #e0e7ff; color: #4f46e5; padding: 5px 10px; border-radius: 6px; text-decoration: none; font-size: 14px;">Visualizar</a>` : ''}
|
||||
${!isDir ? `<a href="${link}" download="${item}" class="btn btn-secondary btn-sm" style="background: #fee2e2; color: #ef4444; padding: 5px 10px; border-radius: 6px; text-decoration: none; font-size: 14px; margin-left: 5px;">Baixar</a>` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Arquivos Clonados - ${folderName}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Inter', sans-serif; background: #f3f4f6; margin: 0; padding: 40px 20px; color: #1f2937; }
|
||||
.container { max-width: 1000px; margin: 0 auto; background: white; border-radius: 16px; box-shadow: 0 10px 25px rgba(0,0,0,0.05); overflow: hidden; padding: 30px; }
|
||||
.header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e5e7eb; padding-bottom: 20px; margin-bottom: 25px; }
|
||||
.header h1 { font-size: 24px; margin: 0; color: #111827; display: flex; align-items: center; gap: 12px; }
|
||||
.actions { display: flex; gap: 12px; }
|
||||
.btn { padding: 10px 20px; border-radius: 8px; font-weight: 600; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; font-size: 14px; transition: all 0.2s; }
|
||||
.btn-primary { background: #4f46e5; color: white; }
|
||||
.btn-primary:hover { background: #4338ca; }
|
||||
.btn-secondary { background: #f3f4f6; color: #374151; }
|
||||
.btn-secondary:hover { background: #e5e7eb; }
|
||||
table { width: 100%; border-collapse: collapse; text-align: left; }
|
||||
th { padding: 16px; background: #f9fafb; font-weight: 600; color: #374151; border-bottom: 1px solid #e5e7eb; font-size: 14px; }
|
||||
td { padding: 16px; border-bottom: 1px solid #e5e7eb; font-size: 15px; }
|
||||
tr:hover { background: #f9fafb; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1><i class="fas fa-folder-open" style="color: #4f46e5;"></i> ${folderName}</h1>
|
||||
<div class="actions">
|
||||
<a href="/${folderName}/index.html" target="_blank" class="btn btn-primary"><i class="fas fa-external-link-alt"></i> Abrir Site Clonado</a>
|
||||
<a href="/" class="btn btn-secondary"><i class="fas fa-arrow-left"></i> Voltar ao Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome do Ficheiro</th>
|
||||
<th>Tamanho</th>
|
||||
<th>Modificado em</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${filesHtml}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
res.send(html);
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar pasta:', error);
|
||||
res.status(500).send('<h1>Erro interno do servidor</h1>');
|
||||
}
|
||||
});
|
||||
|
||||
// Abrir pasta do clone (mantém compatibilidade legada e retorna a URL do Web Browse)
|
||||
app.post('/api/open-folder', (req, res) => {
|
||||
try {
|
||||
const { path: folderPath } = req.body;
|
||||
@@ -193,14 +532,10 @@ app.post('/api/open-folder', (req, res) => {
|
||||
return res.status(400).json({ error: 'Caminho da pasta é obrigatório' });
|
||||
}
|
||||
|
||||
// Abrir pasta no Windows Explorer
|
||||
exec(`explorer "${folderPath}"`, (error) => {
|
||||
if (error) {
|
||||
console.error('Erro ao abrir pasta:', error);
|
||||
return res.status(500).json({ error: 'Não foi possível abrir a pasta' });
|
||||
}
|
||||
res.json({ success: true, message: 'Pasta aberta com sucesso' });
|
||||
});
|
||||
const folderName = path.basename(folderPath);
|
||||
const browseUrl = `/api/browse/${folderName}`;
|
||||
|
||||
res.json({ success: true, message: 'Pasta acessível via Web', url: browseUrl });
|
||||
} catch (error) {
|
||||
console.error('Erro ao abrir pasta:', error);
|
||||
res.status(500).json({ error: 'Erro interno do servidor' });
|
||||
@@ -215,20 +550,10 @@ app.post('/api/open-site', (req, res) => {
|
||||
return res.status(400).json({ error: 'Caminho do site é obrigatório' });
|
||||
}
|
||||
|
||||
// Extrair apenas o nome da pasta (último segmento do caminho)
|
||||
const folderName = path.basename(sitePath);
|
||||
const siteUrl = `http://localhost:${PORT}/${folderName}/index.html`;
|
||||
const siteUrl = `/${folderName}/index.html`;
|
||||
|
||||
console.log(`🌍 Opening site via HTTP: ${siteUrl}`);
|
||||
|
||||
// Abrir URL no navegador padrão
|
||||
exec(`start "" "${siteUrl}"`, (error) => {
|
||||
if (error) {
|
||||
console.error('Erro ao abrir site:', error);
|
||||
return res.status(500).json({ error: 'Não foi possível abrir o site via HTTP' });
|
||||
}
|
||||
res.json({ success: true, message: 'Site aberto via HTTP com sucesso', url: siteUrl });
|
||||
});
|
||||
res.json({ success: true, message: 'Site aberto com sucesso', url: siteUrl });
|
||||
} catch (error) {
|
||||
console.error('Erro ao abrir site:', error);
|
||||
res.status(500).json({ error: 'Erro interno do servidor' });
|
||||
@@ -281,6 +606,239 @@ app.get('/index', (req, res) => {
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
// List optimized pages
|
||||
app.get('/api/optimized-pages', (req, res) => {
|
||||
try {
|
||||
const clonesDir = path.join(__dirname, '..', 'cloned-sites');
|
||||
if (!fs.existsSync(clonesDir)) {
|
||||
return res.json({ success: true, pages: [] });
|
||||
}
|
||||
|
||||
const items = fs.readdirSync(clonesDir);
|
||||
const optimized = items
|
||||
.filter(item => {
|
||||
const fullPath = path.join(clonesDir, item);
|
||||
return fs.statSync(fullPath).isDirectory() && item.endsWith('_designer');
|
||||
})
|
||||
.map(item => ({
|
||||
name: item,
|
||||
directory: item
|
||||
}));
|
||||
|
||||
res.json({ success: true, pages: optimized });
|
||||
} catch (error) {
|
||||
console.error('Error listing optimized pages:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// AI Agent Route (MiniMax)
|
||||
app.post('/api/ai/agent', async (req, res) => {
|
||||
const { systemPrompt, userMessage } = req.body;
|
||||
const apiKey = process.env.MINIMAX_API_KEY || 'sk-cp-iWzSsdzjVy0tT5dnvv_sGrAlv3Nw7kkrZ8hPK0KjbNeSRQxIY1O6AhkQAq_yrYV1XPJY1TH6ZQeOlJZd-Zi_elkJhef9PcTSzn2m9bcKIxezarM19BREcVM';
|
||||
|
||||
if (!apiKey) {
|
||||
return res.status(500).json({ error: 'MINIMAX_API_KEY is missing' });
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post('https://api.minimax.io/v1/chat/completions', {
|
||||
model: "MiniMax-M2.7",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt || "Você é um assistente de inteligência artificial útil." },
|
||||
{ role: "user", content: userMessage }
|
||||
],
|
||||
max_tokens: 2048
|
||||
}, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 90000 // 90s
|
||||
});
|
||||
|
||||
let reply = response.data.choices?.[0]?.message?.content || 'Processamento concluído, porém sem retorno.';
|
||||
// Strip <think> reasoning tags if present
|
||||
reply = reply.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
||||
|
||||
res.json({ success: true, data: reply });
|
||||
} catch (error) {
|
||||
console.error('MiniMax AI Error:', error.response?.data || error.message);
|
||||
res.status(500).json({ error: error.message, details: error.response?.data });
|
||||
}
|
||||
});
|
||||
|
||||
// AI Inject Redesign Route (Pilar 1 - Agente Designer Real)
|
||||
app.post('/api/ai/inject-redesign', (req, res) => {
|
||||
const { cloneDir, appliedStyles, elementsRedesigned, outputDir, agentName } = req.body;
|
||||
if (!cloneDir) return res.status(400).json({ error: 'cloneDir is required' });
|
||||
|
||||
let originalFolderName = '';
|
||||
let folderPath = '';
|
||||
let newFolderPath = '';
|
||||
|
||||
// Sempre usar o diretório interno (cloned-sites)
|
||||
const cleanDir = cloneDir.endsWith('/') ? cloneDir.slice(0, -1) : cloneDir;
|
||||
originalFolderName = cleanDir.split('/').pop();
|
||||
folderPath = path.join(__dirname, '..', 'cloned-sites', originalFolderName);
|
||||
newFolderPath = path.join(__dirname, '..', 'cloned-sites', `${originalFolderName}_${agentName || 'designer'}`);
|
||||
|
||||
if (!fs.existsSync(folderPath)) {
|
||||
return res.status(404).json({ error: 'Cloned site folder not found' });
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Criar o arquivo CSS de Redesign Ultra-Moderno (__ai_redesign.css)
|
||||
const cssContent = `
|
||||
/* ==========================================================================
|
||||
BRAINSTEEL WEB - AI ULTRA-MODERN REDESIGN (AGENTE DESIGNER)
|
||||
========================================================================== */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;600;800&display=swap');
|
||||
|
||||
:root {
|
||||
--ai-bg-dark: #0b0f19;
|
||||
--ai-bg-card: rgba(18, 24, 38, 0.7);
|
||||
--ai-border-glass: rgba(255, 255, 255, 0.08);
|
||||
--ai-accent-glow: #3b82f6;
|
||||
--ai-accent-gradient: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 50%, #ec4899 100%);
|
||||
--ai-text-main: #f3f4f6;
|
||||
--ai-text-muted: #9ca3af;
|
||||
}
|
||||
|
||||
/* Força Dark Mode elegante e tipografia moderna no body e containers principais */
|
||||
body, main, #root, #app, .app, .main, .container, .wrapper, header, footer, section {
|
||||
background-color: var(--ai-bg-dark) !important;
|
||||
color: var(--ai-text-main) !important;
|
||||
font-family: 'Inter', sans-serif !important;
|
||||
}
|
||||
|
||||
/* Glassmorphism e Glow em Cards, Painéis, Cabeçalhos e Rodapés */
|
||||
div[class*="card"], div[class*="panel"], div[class*="box"], div[class*="container"], div[class*="item"], header, footer, nav, .navbar, .header, .footer {
|
||||
background: var(--ai-bg-card) !important;
|
||||
backdrop-filter: blur(16px) !important;
|
||||
-webkit-backdrop-filter: blur(16px) !important;
|
||||
border: 1px solid var(--ai-border-glass) !important;
|
||||
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.5) !important;
|
||||
border-radius: 16px !important;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
}
|
||||
|
||||
div[class*="card"]:hover, div[class*="item"]:hover {
|
||||
transform: translateY(-5px) !important;
|
||||
border-color: rgba(59, 130, 246, 0.4) !important;
|
||||
box-shadow: 0 20px 40px -15px rgba(59, 130, 246, 0.3) !important;
|
||||
}
|
||||
|
||||
/* Títulos com Gradiente Vibrante (Outfit Font) */
|
||||
h1, h2, h3, h4, h5, .title, .heading, .header-title {
|
||||
font-family: 'Outfit', sans-serif !important;
|
||||
background: var(--ai-accent-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
background-clip: text !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: -0.03em !important;
|
||||
}
|
||||
|
||||
/* Botões Ultra-Modernos com Gradiente e Animação de Pulso */
|
||||
button, .btn, a[class*="btn"], input[type="submit"], input[type="button"] {
|
||||
background: var(--ai-accent-gradient) !important;
|
||||
color: #ffffff !important;
|
||||
border: none !important;
|
||||
border-radius: 50px !important;
|
||||
padding: 12px 28px !important;
|
||||
font-family: 'Inter', sans-serif !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.02em !important;
|
||||
box-shadow: 0 10px 25px -5px rgba(139, 92, 246, 0.5) !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.3s ease !important;
|
||||
text-transform: none !important;
|
||||
-webkit-text-fill-color: #ffffff !important;
|
||||
}
|
||||
|
||||
button:hover, .btn:hover, a[class*="btn"]:hover {
|
||||
transform: scale(1.05) translateY(-2px) !important;
|
||||
box-shadow: 0 15px 35px -5px rgba(236, 72, 153, 0.6) !important;
|
||||
}
|
||||
|
||||
/* Inputs e Textareas com Efeito Glass */
|
||||
input, select, textarea, .input, .form-control {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
border: 1px solid var(--ai-border-glass) !important;
|
||||
color: var(--ai-text-main) !important;
|
||||
border-radius: 12px !important;
|
||||
padding: 12px 16px !important;
|
||||
font-family: 'Inter', sans-serif !important;
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus {
|
||||
outline: none !important;
|
||||
border-color: var(--ai-accent-glow) !important;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3) !important;
|
||||
}
|
||||
|
||||
/* Links e Textos Secundários */
|
||||
a {
|
||||
color: var(--ai-accent-glow) !important;
|
||||
text-decoration: none !important;
|
||||
transition: color 0.2s !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #8b5cf6 !important;
|
||||
}
|
||||
|
||||
p, span, li, label, td, th {
|
||||
color: var(--ai-text-muted) !important;
|
||||
line-height: 1.6 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
// Copy the original folder to the new folder
|
||||
exec(`cp -r "${folderPath}" "${newFolderPath}"`, (copyErr) => {
|
||||
if (copyErr) {
|
||||
console.error('Error copying folder for agent redesign:', copyErr);
|
||||
return res.status(500).json({ error: 'Failed to create new agent folder' });
|
||||
}
|
||||
|
||||
// 1. Criar o arquivo CSS de Redesign Ultra-Moderno (__ai_redesign.css) no novo diretório
|
||||
const cssPath = path.join(newFolderPath, '__ai_redesign.css');
|
||||
fs.writeFileSync(cssPath, cssContent, 'utf8');
|
||||
|
||||
// 2. Injetar a tag <link> no index.html do novo clone
|
||||
const indexPath = path.join(newFolderPath, 'index.html');
|
||||
if (fs.existsSync(indexPath)) {
|
||||
let html = fs.readFileSync(indexPath, 'utf8');
|
||||
const linkTag = `\n<link rel="stylesheet" href="/__ai_redesign.css" id="__AI_REDESIGN_LINK__">\n`;
|
||||
|
||||
if (!html.includes('__ai_redesign.css')) {
|
||||
if (html.includes('</head>')) {
|
||||
html = html.replace('</head>', linkTag + '</head>');
|
||||
} else {
|
||||
html = linkTag + html;
|
||||
}
|
||||
fs.writeFileSync(indexPath, html, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Sincronizar o novo diretório com o host
|
||||
const hostDir = outputDir || '/root/Desktop/CLONE_SITE';
|
||||
const containerId = require('os').hostname();
|
||||
exec(`mkdir -p "${hostDir}" && cp -r "${newFolderPath}" "${hostDir}/" 2>/dev/null || docker cp ${containerId}:"${newFolderPath}" "${hostDir}/" 2>/dev/null`, (syncErr) => {
|
||||
if (syncErr) console.log('⚠️ Redesign sync to host failed:', syncErr.message);
|
||||
else console.log('✅ Redesign synced to host output dir:', path.basename(newFolderPath));
|
||||
});
|
||||
|
||||
res.json({ success: true, message: 'AI Redesign CSS injected successfully', newFolderName: path.basename(newFolderPath) });
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error injecting AI redesign:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Handle 404s
|
||||
app.use((req, res) => {
|
||||
res.status(404).json({
|
||||
|
||||
Reference in New Issue
Block a user