F-002 fix: Remove secrets and externalize config
This commit is contained in:
269
project/web/index/new/describe.php
Executable file
269
project/web/index/new/describe.php
Executable file
@@ -0,0 +1,269 @@
|
||||
<?php include ('./inc/header.php'); ?>
|
||||
|
||||
<?php
|
||||
ini_set('max_execution_time', 120);
|
||||
set_time_limit(120);
|
||||
|
||||
$producto = "";
|
||||
$ean = "";
|
||||
$autoGenerar = false;
|
||||
$formato = isset($_POST['formato']) ? $_POST['formato'] : 'plano';
|
||||
|
||||
// Si viene por URL
|
||||
if (isset($_GET['name']) && !empty($_GET['name'])) {
|
||||
$producto = urldecode($_GET['name']);
|
||||
$autoGenerar = true;
|
||||
}
|
||||
|
||||
// Si viene por POST
|
||||
if (isset($_POST['prompt'])) {
|
||||
$producto = trim($_POST['prompt']);
|
||||
$autoGenerar = true;
|
||||
}
|
||||
|
||||
// Detectar EAN (8 a 13 dígitos seguidos)
|
||||
if (preg_match('/\b\d{8,13}\b/', $producto, $matches)) {
|
||||
$ean = $matches[0];
|
||||
}
|
||||
|
||||
if ($autoGenerar && !empty($producto)) {
|
||||
|
||||
// Prompt según formato seleccionado
|
||||
if ($formato === 'plano') {
|
||||
$prompt = "
|
||||
Eres un redactor SEO experto en productos naturales, ecológicos y saludables.
|
||||
Genera una descripción en texto plano sin formato ni negritas ni guiones, lista para pegar en CKEditor.
|
||||
|
||||
Sobre el producto: \"$producto\"
|
||||
|
||||
Instrucciones:
|
||||
1. Prioriza la información del fabricante o distribuidor oficial.
|
||||
2. Si el producto tiene un código EAN, utilízalo para obtener información nutricional en OpenFoodFacts.
|
||||
3. No menciones ni enlaces fuentes externas.
|
||||
4. No inventes datos no verificables.
|
||||
|
||||
Estructura del texto:
|
||||
Descripción:
|
||||
Ingredientes:
|
||||
Información nutricional (por 100 g):
|
||||
Beneficios para la salud:
|
||||
Por qué deberías probarlo:
|
||||
Keywords:
|
||||
|
||||
Reglas:
|
||||
- No uses HTML, JSON ni emojis.
|
||||
- No repitas el nombre del producto en exceso.
|
||||
";
|
||||
} else {
|
||||
$prompt = "
|
||||
Eres un redactor SEO especializado en productos naturales y saludables.
|
||||
Genera una descripción optimizada, estructurada y lista para publicación web.
|
||||
|
||||
Producto: \"$producto\"
|
||||
|
||||
Prioriza datos del fabricante o distribuidor oficial, y si el producto tiene un código EAN, usa OpenFoodFacts para complementar la información nutricional.
|
||||
No incluyas ni menciones enlaces externos, ni nombres de tiendas online o marketplaces.
|
||||
|
||||
Estructura del texto:
|
||||
|
||||
### Descripción
|
||||
Breve texto atractivo sobre origen, uso y beneficios.
|
||||
|
||||
### Ingredientes
|
||||
Lista completa y verificada.
|
||||
|
||||
### Información nutricional (por 100 g)
|
||||
Calorías, grasas, hidratos, azúcares, proteínas, fibra y sal. Indica si provienen del fabricante o fuente pública.
|
||||
|
||||
### Beneficios para la salud
|
||||
Texto breve explicando las propiedades y usos.
|
||||
|
||||
### Por qué deberías probarlo
|
||||
Cierre aspiracional, natural y coherente.
|
||||
|
||||
### Keywords
|
||||
Lista de términos relevantes separados por comas.
|
||||
|
||||
Reglas:
|
||||
- No uses HTML ni JSON.
|
||||
- No uses emojis.
|
||||
- No menciones fuentes ni enlaces externos.
|
||||
";
|
||||
}
|
||||
|
||||
$respuesta = obtener_respuesta($prompt);
|
||||
|
||||
// Detectar enlaces válidos (solo OpenFoodFacts o fabricante)
|
||||
$fuentes = [];
|
||||
|
||||
// --- Verificar si el EAN existe realmente en OpenFoodFacts ---
|
||||
if (!empty($ean)) {
|
||||
$url_off = "https://world.openfoodfacts.org/product/$ean";
|
||||
$headers = @get_headers($url_off);
|
||||
if ($headers && strpos($headers[0], '200') !== false) {
|
||||
$fuentes[] = "Información verificada en OpenFoodFacts: $url_off";
|
||||
}
|
||||
}
|
||||
|
||||
// --- Verificar si existe ficha oficial del fabricante (ejemplo: Terpenic Labs) ---
|
||||
$producto_slug = strtolower(str_replace(' ', '-', $producto));
|
||||
$url_fabricante = "https://www.terpenic.com/product-page/" . urlencode($producto_slug);
|
||||
$headers = @get_headers($url_fabricante);
|
||||
if ($headers && strpos($headers[0], '200') !== false) {
|
||||
$fuentes[] = "Ficha oficial del fabricante: $url_fabricante";
|
||||
}
|
||||
|
||||
// --- Agregar los enlaces válidos al final del texto (copiable) ---
|
||||
if (!empty($fuentes)) {
|
||||
$respuesta .= "\n\n" . implode("\n", $fuentes);
|
||||
}
|
||||
}
|
||||
|
||||
function obtener_respuesta($prompt) {
|
||||
$apiKey = trim((string) legacy_config('openai.api_key', ''));
|
||||
$model = legacy_config('openai.model', 'gpt-4o-mini');
|
||||
$endpoint = legacy_config('openai.endpoint', 'https://api.openai.com/v1/chat/completions');
|
||||
|
||||
if ($apiKey === '' || strpos($apiKey, 'CHANGE_ME_') === 0) {
|
||||
return "⚠️ Configura openai.api_key en config/local.php.";
|
||||
}
|
||||
|
||||
$ch = curl_init($endpoint);
|
||||
$data = array(
|
||||
'model' => $model,
|
||||
'messages' => array(
|
||||
array('role' => 'system', 'content' => 'Eres un redactor SEO experto en e-commerce.'),
|
||||
array('role' => 'user', 'content' => $prompt)
|
||||
),
|
||||
'temperature' => 0.6,
|
||||
'max_tokens' => 1200
|
||||
);
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $apiKey
|
||||
));
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
return "⚠️ Error de conexión: " . curl_error($ch);
|
||||
}
|
||||
|
||||
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($http_status !== 200) {
|
||||
return "⚠️ Error HTTP $http_status — el servidor no respondió correctamente.";
|
||||
}
|
||||
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if (isset($response['choices'][0]['message']['content'])) {
|
||||
return $response['choices'][0]['message']['content'];
|
||||
} else {
|
||||
return "⚠️ No se pudo generar respuesta. Detalle:\n" . json_encode($response, JSON_PRETTY_PRINT);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<style>
|
||||
.loader-container {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.loader {
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #0074D9;
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin { 0% { transform: rotate(0deg);} 100% { transform: rotate(360deg);} }
|
||||
.loading-text {
|
||||
margin-top: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
color: #333;
|
||||
animation: fadeText 1.5s infinite;
|
||||
}
|
||||
@keyframes fadeText {
|
||||
0%,100% {opacity: 0.2;} 50% {opacity: 1;}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<form method="POST" onsubmit="showLoader()">
|
||||
<div class="row" style="margin-top: 20px">
|
||||
<div class="ten columns">
|
||||
<input class="u-full-width" type="text" name="prompt" id="prompt"
|
||||
placeholder="Ingresa el nombre del producto"
|
||||
value="<?php echo htmlspecialchars($producto); ?>">
|
||||
</div>
|
||||
<div class="two columns">
|
||||
<button class="button button-primary u-full-width" type="submit">Generar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top:10px;">
|
||||
<label>
|
||||
<input type="checkbox" name="formato" value="formato" <?php echo ($formato==='formato')?'checked':''; ?>>
|
||||
<span>Con formato para SEO</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="loader" class="loader-container">
|
||||
<div class="loader"></div>
|
||||
<div class="loading-text">Generando contenido...</div>
|
||||
</div>
|
||||
|
||||
<?php if ($autoGenerar && !isset($respuesta)) { ?>
|
||||
<script>document.addEventListener("DOMContentLoaded",()=>{document.getElementById("loader").style.display="flex";});</script>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (isset($respuesta)) { ?>
|
||||
<script>document.addEventListener("DOMContentLoaded",()=>{document.getElementById("loader").style.display="none";});</script>
|
||||
|
||||
<div class="row" style="margin-top:20px;">
|
||||
<div class="twelve columns">
|
||||
<pre id="texto-copiar" style="background:#f9f9f9;padding:15px;border-radius:6px;white-space:pre-wrap;font-size:1rem;"><?php echo htmlspecialchars($respuesta); ?></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top:10px;">
|
||||
<div class="two columns">
|
||||
<button class="button button-primary u-full-width" onclick="refreshPage()">
|
||||
<i class="fa fa-refresh fa-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ten columns">
|
||||
<button class="u-full-width" onclick="copiarTexto()">Copiar texto</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showLoader(){document.getElementById("loader").style.display="flex";}
|
||||
function copiarTexto(){
|
||||
var e=document.getElementById("texto-copiar");
|
||||
var t=e.innerText;
|
||||
navigator.clipboard.writeText(t);
|
||||
e.style.backgroundColor="#ffffa0";
|
||||
setTimeout(()=>{e.style.backgroundColor="";alert("Texto copiado al portapapeles");},500);
|
||||
}
|
||||
function refreshPage(){location.reload();}
|
||||
</script>
|
||||
|
||||
<?php include ('./inc/footer.php'); ?>
|
||||
|
||||
Reference in New Issue
Block a user