feat: endpoints usan api_base_url como base, rutas relativas en UI
- Banner en tab Endpoints muestra la URL base de General - Inputs de URL aceptan rutas relativas (?param=x o /ruta) - Preview live de la URL completa (base+ruta) bajo cada input - Backend prepend api_base_url en test y sync si URL no es absoluta - Compatible hacia atrás: URLs absolutas existentes siguen funcionando Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
bfbc70451d
commit
43933bfe3a
@@ -1113,12 +1113,23 @@ HTML;
|
|||||||
$epStmt->execute([$id]);
|
$epStmt->execute([$id]);
|
||||||
$allEps = $epStmt->fetchAll(\PDO::FETCH_ASSOC);
|
$allEps = $epStmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$baseRaw = rtrim($company['api_base_url'] ?? '', '/');
|
||||||
|
$baseHtml = self::h($baseRaw);
|
||||||
|
|
||||||
$epRows = '';
|
$epRows = '';
|
||||||
foreach ($allEps as $ep) {
|
foreach ($allEps as $ep) {
|
||||||
$epKey = self::h($ep['endpoint_key']);
|
$epKey = self::h($ep['endpoint_key']);
|
||||||
$epName = self::h($ep['name'] ?? '');
|
$epName = self::h($ep['name'] ?? '');
|
||||||
$epUrl = self::h($ep['url'] ?? '');
|
$epUrl = self::h($ep['url'] ?? '');
|
||||||
$epMeth = $ep['method'] ?? 'GET';
|
$epMeth = $ep['method'] ?? 'GET';
|
||||||
|
// Preview full URL
|
||||||
|
$rawUrl = $ep['url'] ?? '';
|
||||||
|
if ($rawUrl === '' || str_starts_with($rawUrl, 'http')) {
|
||||||
|
$previewUrl = self::h($rawUrl);
|
||||||
|
} else {
|
||||||
|
$sep = str_starts_with($rawUrl, '?') ? '' : '/';
|
||||||
|
$previewUrl = self::h($baseRaw . $sep . ltrim($rawUrl, '/'));
|
||||||
|
}
|
||||||
$epDir = $ep['direction'] ?? 'download';
|
$epDir = $ep['direction'] ?? 'download';
|
||||||
$epParams = self::h($ep['params'] ?? '');
|
$epParams = self::h($ep['params'] ?? '');
|
||||||
$epAct = $ep['is_active'] ? 'checked' : '';
|
$epAct = $ep['is_active'] ? 'checked' : '';
|
||||||
@@ -1215,9 +1226,10 @@ VR;
|
|||||||
<option {$mSel('GET')}>GET</option><option {$mSel('POST')}>POST</option>
|
<option {$mSel('GET')}>GET</option><option {$mSel('POST')}>POST</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td style="min-width:200px">
|
<td style="min-width:220px">
|
||||||
<input type="text" value="{$epUrl}" id="epu_{$epId}" placeholder="https://..." oninput="onUrlChange({$epId})" style="width:100%;font-size:12px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px">
|
<input type="text" value="{$epUrl}" id="epu_{$epId}" placeholder="?peticion=xxx o /ruta" oninput="onUrlChange({$epId})" style="width:100%;font-size:12px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px">
|
||||||
<div style="font-size:10px;color:#9ca3af;margin-top:2px">Usa {'{var}'} para variables. {$lastAt}</div>
|
<div id="ep-preview-{$epId}" style="font-size:10px;color:#6b7280;font-family:monospace;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="{$previewUrl}">{$previewUrl}</div>
|
||||||
|
<div style="font-size:10px;color:#9ca3af;margin-top:1px">Usa {'{var}'} para variables. {$lastAt}</div>
|
||||||
</td>
|
</td>
|
||||||
<td><input type="checkbox" {$epAct} id="epa_{$epId}" title="Activo"></td>
|
<td><input type="checkbox" {$epAct} id="epa_{$epId}" title="Activo"></td>
|
||||||
<td style="white-space:nowrap">
|
<td style="white-space:nowrap">
|
||||||
@@ -1246,15 +1258,21 @@ ROW;
|
|||||||
|
|
||||||
$epHtml = <<<EP
|
$epHtml = <<<EP
|
||||||
<div class="card-h" style="margin-bottom:12px">🔌 Endpoints API</div>
|
<div class="card-h" style="margin-bottom:12px">🔌 Endpoints API</div>
|
||||||
|
<div style="background:#fffbeb;border:1px solid #fde68a;border-radius:8px;padding:10px 14px;margin-bottom:14px;font-size:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
||||||
|
<span>🔗 <strong>URL base:</strong></span>
|
||||||
|
<code style="background:#fff8e1;padding:2px 6px;border-radius:4px;font-size:12px">{$baseHtml}</code>
|
||||||
|
<a href="/admin/company/edit?id={$id}&tab=general" style="color:#1e40af;font-size:11px">cambiar en General →</a>
|
||||||
|
<span style="color:#92400e;font-size:11px">· Los endpoints usan rutas relativas a esta URL</span>
|
||||||
|
</div>
|
||||||
<p style="font-size:12px;color:#7a8291;margin-bottom:14px">
|
<p style="font-size:12px;color:#7a8291;margin-bottom:14px">
|
||||||
Define los endpoints del ERP. Usa <code>{'{variable}'}</code> en la URL para variables que el bot pedirá al usuario.<br>
|
Escribe solo la ruta o sufijo: <code>?peticion=produccion</code> o <code>/mi/ruta</code>. Se combina con la URL base.<br>
|
||||||
Ejemplo: <code>?peticion=produccion&desde={'{desde}'}&hasta={'{hasta}'}</code>
|
Usa <code>{'{variable}'}</code> para variables que el bot pedirá al usuario. Ejemplo: <code>?desde={'{desde}'}&hasta={'{hasta}'}</code>
|
||||||
</p>
|
</p>
|
||||||
<div class="card" style="overflow-x:auto">
|
<div class="card" style="overflow-x:auto">
|
||||||
<table style="width:100%;min-width:700px">
|
<table style="width:100%;min-width:700px">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="font-size:12px;text-align:left">
|
<tr style="font-size:12px;text-align:left">
|
||||||
<th>Key</th><th>Nombre</th><th>Dirección</th><th>Método</th><th>URL</th><th>Activo</th><th></th>
|
<th>Key</th><th>Nombre</th><th>Dirección</th><th>Método</th><th>Ruta / sufijo</th><th>Activo</th><th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="epTableBody">{$epRows}</tbody>
|
<tbody id="epTableBody">{$epRows}</tbody>
|
||||||
@@ -1290,8 +1308,9 @@ ROW;
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>URL</label>
|
<label>Ruta / sufijo</label>
|
||||||
<input type="text" id="newEpUrl" placeholder="https://app.palmas360.com/...?peticion=xxx">
|
<input type="text" id="newEpUrl" placeholder="?peticion=xxx o /mi/ruta" oninput="updateNewEpPreview()">
|
||||||
|
<div id="newEpPreview" style="font-size:11px;color:#6b7280;font-family:monospace;margin-top:4px"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="newEpMsg" style="margin-bottom:8px"></div>
|
<div id="newEpMsg" style="margin-bottom:8px"></div>
|
||||||
<button class="btn-primary" onclick="addNewEp({$id})">Agregar</button>
|
<button class="btn-primary" onclick="addNewEp({$id})">Agregar</button>
|
||||||
@@ -1620,8 +1639,33 @@ function collectVarConfigs(epId) {
|
|||||||
return JSON.stringify(configs);
|
return JSON.stringify(configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BASE_URL = '{$baseHtml}';
|
||||||
|
|
||||||
|
function buildFullUrl(path) {
|
||||||
|
if (!path || path.startsWith('http')) return path;
|
||||||
|
const base = BASE_URL.replace(/\/$/, '');
|
||||||
|
const sep = path.startsWith('?') ? '' : '/';
|
||||||
|
return base + sep + path.replace(/^\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEpPreview(epId) {
|
||||||
|
const path = document.getElementById('epu_'+epId)?.value || '';
|
||||||
|
const el = document.getElementById('ep-preview-'+epId);
|
||||||
|
if (!el) return;
|
||||||
|
const full = buildFullUrl(path);
|
||||||
|
el.textContent = full || '';
|
||||||
|
el.title = full || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNewEpPreview() {
|
||||||
|
const path = document.getElementById('newEpUrl')?.value || '';
|
||||||
|
const el = document.getElementById('newEpPreview');
|
||||||
|
if (el) el.textContent = buildFullUrl(path) || '';
|
||||||
|
}
|
||||||
|
|
||||||
// When URL changes, refresh the vars section
|
// When URL changes, refresh the vars section
|
||||||
function onUrlChange(epId) {
|
function onUrlChange(epId) {
|
||||||
|
updateEpPreview(epId);
|
||||||
const url = document.getElementById('epu_'+epId).value;
|
const url = document.getElementById('epu_'+epId).value;
|
||||||
const matches = [...url.matchAll(/\{([^}]+)\}/g)].map(m => m[1]);
|
const matches = [...url.matchAll(/\{([^}]+)\}/g)].map(m => m[1]);
|
||||||
const vrRows = document.getElementById('vr-rows-'+epId);
|
const vrRows = document.getElementById('vr-rows-'+epId);
|
||||||
@@ -1797,9 +1841,15 @@ HTML;
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Llamar al endpoint
|
// Llamar al endpoint — prepend base URL si es ruta relativa
|
||||||
|
$epUrl = $ep['url'];
|
||||||
|
if (!str_starts_with($epUrl, 'http')) {
|
||||||
|
$base = rtrim($company['api_base_url'] ?? '', '/');
|
||||||
|
$sep = str_starts_with($epUrl, '?') ? '' : '/';
|
||||||
|
$epUrl = $base . $sep . ltrim($epUrl, '/');
|
||||||
|
}
|
||||||
$apiKey = $company['api_key'] ?? '';
|
$apiKey = $company['api_key'] ?? '';
|
||||||
$ch = curl_init($ep['url']);
|
$ch = curl_init($epUrl);
|
||||||
$opts = [
|
$opts = [
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_TIMEOUT => 15,
|
CURLOPT_TIMEOUT => 15,
|
||||||
@@ -2050,6 +2100,13 @@ HTML;
|
|||||||
$company = CompanyRepository::findById($companyId);
|
$company = CompanyRepository::findById($companyId);
|
||||||
$apiKey = $company['api_key'] ?? '';
|
$apiKey = $company['api_key'] ?? '';
|
||||||
|
|
||||||
|
// Prepend api_base_url for relative paths
|
||||||
|
if ($url !== '' && !str_starts_with($url, 'http')) {
|
||||||
|
$base = rtrim($company['api_base_url'] ?? '', '/');
|
||||||
|
$sep = str_starts_with($url, '?') ? '' : '/';
|
||||||
|
$url = $base . $sep . ltrim($url, '/');
|
||||||
|
}
|
||||||
|
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
$opts = [
|
$opts = [
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
|||||||
Reference in New Issue
Block a user