Files
html/api/em-api.php
2026-04-17 02:50:02 +02:00

302 lines
15 KiB
PHP

<?php
/**
* /api/em/ router — EM endpoints (GODMODE 17avr)
* Handles: agents-registry, vsm, bpmn-routines, dmaic, kpi/live, poc/start, tenant/bootstrap, plans
*/
header("Content-Type: application/json; charset=utf-8");
header("Access-Control-Allow-Origin: *");
$DB_HOST = "127.0.0.1";
$DB_PORT = 5432;
$DB_NAME = "adx_system";
$DB_USER = "admin";
$DB_PASS = "admin123";
try {
$pdo = new PDO("pgsql:host=$DB_HOST;port=$DB_PORT;dbname=$DB_NAME", $DB_USER, $DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (Exception $e) {
// Fallback to local if S95 unreachable
try {
$pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", $DB_USER, $DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (Exception $e2) {
http_response_code(500);
echo json_encode(["error" => "db-unreachable"]);
exit;
}
}
$uri = parse_url($_SERVER["REQUEST_URI"] ?? "", PHP_URL_PATH);
$path = preg_replace("#^/api/em/?#", "", $uri);
$parts = explode("/", trim($path, "/"));
$endpoint = $parts[0] ?? "";
function audit($pdo, $action, $target = null, $payload = []) {
$tenant = $_GET["tenant"] ?? $_POST["tenant"] ?? "weval";
try {
$stmt = $pdo->prepare("INSERT INTO weval.audit_log (tenant_id, actor, action, target, payload, ip) VALUES (?,?,?,?,?,?)");
$stmt->execute([$tenant, "em-api", $action, $target, json_encode($payload), $_SERVER["REMOTE_ADDR"] ?? ""]);
} catch (Exception $e) {}
}
switch ($endpoint) {
case "agents-registry":
$tenant = $_GET["tenant"] ?? "weval";
$dept = $_GET["dept"] ?? null;
$sql = "SELECT id,name,dept,tier,layer_soa,skills,routines,status,source FROM weval.agent_registry WHERE tenant_id=?";
$params = [$tenant];
if ($dept) { $sql .= " AND dept=?"; $params[] = $dept; }
$sql .= " ORDER BY tier, name";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) {
$r["skills"] = json_decode($r["skills"], true);
$r["routines"] = json_decode($r["routines"], true);
}
echo json_encode(["tenant" => $tenant, "count" => count($rows), "agents" => $rows]);
break;
case "vsm":
$tenant = $_GET["tenant"] ?? "weval";
if (!empty($parts[1])) {
$stmt = $pdo->prepare("SELECT * FROM weval.vsm_dept WHERE tenant_id=? AND dept_code=?");
$stmt->execute([$tenant, $parts[1]]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) { http_response_code(404); echo json_encode(["error"=>"not-found"]); break; }
$row["kpis"] = json_decode($row["kpis"], true);
$row["agents"] = json_decode($row["agents"], true);
echo json_encode($row);
} else {
$stmt = $pdo->prepare("SELECT dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents FROM weval.vsm_dept WHERE tenant_id=? ORDER BY id");
$stmt->execute([$tenant]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) {
$r["kpis"] = json_decode($r["kpis"], true);
$r["agents"] = json_decode($r["agents"], true);
}
echo json_encode(["tenant"=>$tenant, "count"=>count($rows), "depts"=>$rows]);
}
break;
case "bpmn-routines":
$tenant = $_GET["tenant"] ?? "weval";
$stmt = $pdo->prepare("SELECT id, name, dept, steps, sla_hours, status, mapped_intents FROM weval.bpmn_routines WHERE tenant_id=? ORDER BY id");
$stmt->execute([$tenant]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) {
$r["steps"] = json_decode($r["steps"], true);
$r["mapped_intents"] = json_decode($r["mapped_intents"], true);
}
echo json_encode(["tenant"=>$tenant, "count"=>count($rows), "routines"=>$rows]);
break;
case "dmaic":
$tenant = $parts[1] ?? $_GET["tenant"] ?? "weval";
$vs = $parts[2] ?? $_GET["vs"] ?? null;
if ($vs) {
$stmt = $pdo->prepare("SELECT * FROM weval.dmaic_cycles WHERE tenant_id=? AND vs_id=?");
$stmt->execute([$tenant, $vs]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo json_encode($row ?: ["error"=>"not-found"]);
} else {
$stmt = $pdo->prepare("SELECT id, vs_id, name, phase, progress, updated_at FROM weval.dmaic_cycles WHERE tenant_id=? ORDER BY id");
$stmt->execute([$tenant]);
echo json_encode(["tenant"=>$tenant, "cycles"=>$stmt->fetchAll(PDO::FETCH_ASSOC)]);
}
break;
case "kpi":
if (($parts[1] ?? "") === "live") {
$tenant = $_GET["tenant"] ?? "weval";
$dept = $_GET["dept"] ?? null;
$sql = "SELECT DISTINCT ON (dept, kpi_name) dept, kpi_name, value, unit, ts, source FROM weval.kpi_timeseries WHERE tenant_id=?";
$params = [$tenant];
if ($dept) { $sql .= " AND dept=?"; $params[] = $dept; }
$sql .= " ORDER BY dept, kpi_name, ts DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
echo json_encode(["tenant"=>$tenant, "kpis"=>$stmt->fetchAll(PDO::FETCH_ASSOC)]);
} else {
http_response_code(404);
echo json_encode(["error"=>"unknown-kpi-endpoint"]);
}
break;
case "poc":
if (($parts[1] ?? "") === "start") {
$raw = json_decode(file_get_contents("php://input"), true) ?? $_POST;
$tenant_id = "poc_" . substr(md5(($raw["name"] ?? "demo") . time()), 0, 8);
$name = $raw["name"] ?? "POC Demo";
$email = $raw["email"] ?? "demo@example.com";
$dept = $raw["dept"] ?? "commerce";
try {
$pdo->prepare("INSERT INTO weval.tenants (tenant_id, name, plan_code, phase, contact_email) VALUES (?,?,?,?,?)")->execute([$tenant_id, $name, "poc", "poc", $email]);
// Clone the selected dept VSM
$pdo->prepare("INSERT INTO weval.vsm_dept (tenant_id, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents) SELECT ?, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents FROM weval.vsm_dept WHERE tenant_id='weval' AND dept_code=?")->execute([$tenant_id, $dept]);
// Create DMAIC cycle
$pdo->prepare("INSERT INTO weval.dmaic_cycles (tenant_id, vs_id, name, phase) VALUES (?, ?, ?, 'define')")->execute([$tenant_id, $dept . "-poc", "POC $name - $dept VSM"]);
audit($pdo, "poc_start", $tenant_id, ["email"=>$email, "dept"=>$dept]);
echo json_encode(["ok"=>true, "tenant_id"=>$tenant_id, "plan"=>"poc", "dept"=>$dept, "brain_center_url"=>"/brain-center-tenant.html?t=$tenant_id", "dmaic_url"=>"/dmaic-workbench.html?t=$tenant_id&vs=$dept-poc", "next_steps"=>["Interview DG","Scan process $dept","DMAIC Define","Baseline KPIs","Rapport"]]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(["error"=>$e->getMessage()]);
}
} else {
echo json_encode(["endpoints"=>["/api/em/poc/start"]]);
}
break;
case "plans":
$stmt = $pdo->query("SELECT plan_code, plan_name, tier, setup_fee, monthly_fee, currency, vs_count, max_users, features FROM weval.em_plans WHERE active=true ORDER BY setup_fee");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) $r["features"] = json_decode($r["features"], true);
echo json_encode(["plans"=>$rows]);
break;
case "tenant":
if (($parts[1] ?? "") === "bootstrap") {
$raw = json_decode(file_get_contents("php://input"), true) ?? $_POST;
$tenant_id = $raw["tenant_id"] ?? "demo_" . time();
$plan = $raw["plan"] ?? "mvp";
$name = $raw["name"] ?? "Demo Tenant";
$email = $raw["email"] ?? "demo@example.com";
try {
$pdo->prepare("INSERT INTO weval.tenants (tenant_id, name, plan_code, phase, contact_email) VALUES (?,?,?,?,?) ON CONFLICT (tenant_id) DO UPDATE SET plan_code=EXCLUDED.plan_code, phase=EXCLUDED.phase")->execute([$tenant_id, $name, $plan, $plan, $email]);
// Clone all VSM depts for MVP/Enterprise
if (in_array($plan, ["mvp","enterprise"])) {
$n = $plan === "enterprise" ? 15 : 5;
$pdo->prepare("INSERT INTO weval.vsm_dept (tenant_id, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents) SELECT ?, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents FROM weval.vsm_dept WHERE tenant_id='weval' LIMIT ? ON CONFLICT DO NOTHING")->execute([$tenant_id, $n]);
}
audit($pdo, "tenant_bootstrap", $tenant_id, ["plan"=>$plan]);
echo json_encode(["ok"=>true, "tenant_id"=>$tenant_id, "plan"=>$plan, "brain_center_url"=>"/brain-center-tenant.html?t=$tenant_id"]);
} catch (Exception $e) { http_response_code(500); echo json_encode(["error"=>$e->getMessage()]); }
} else {
$stmt = $pdo->query("SELECT tenant_id, name, plan_code, phase, status FROM weval.tenants ORDER BY created_at DESC LIMIT 50");
echo json_encode(["tenants"=>$stmt->fetchAll(PDO::FETCH_ASSOC)]);
}
break;
case "audit":
$tenant = $_GET["tenant"] ?? "weval";
$limit = min(intval($_GET["limit"] ?? 50), 500);
$stmt = $pdo->prepare("SELECT actor, action, target, ts FROM weval.audit_log WHERE tenant_id=? ORDER BY ts DESC LIMIT ?");
$stmt->execute([$tenant, $limit]);
echo json_encode(["tenant"=>$tenant, "events"=>$stmt->fetchAll(PDO::FETCH_ASSOC)]);
break;
case "erp-connectors":
$stmt = $pdo->query("SELECT code, name, vendor, protocols, modules, auth_type, status, config_schema FROM weval.erp_connectors WHERE status='available' ORDER BY vendor, name");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) {
$r["protocols"] = json_decode($r["protocols"], true);
$r["modules"] = json_decode($r["modules"], true);
$r["config_schema"] = json_decode($r["config_schema"], true);
}
echo json_encode(["count" => count($rows), "connectors" => $rows]);
break;
case "ai-providers":
$stmt = $pdo->query("SELECT code, name, vendor, models, capabilities, endpoint, auth_type, status FROM weval.ai_providers WHERE status='available' ORDER BY vendor, name");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) {
$r["models"] = json_decode($r["models"], true);
$r["capabilities"] = json_decode($r["capabilities"], true);
}
echo json_encode(["count" => count($rows), "providers" => $rows]);
break;
case "industry-templates":
$sector = $_GET["sector"] ?? null;
$sql = "SELECT code, name, sector, vsm_depts, kpis, routines, compliance, description FROM weval.industry_templates";
$params = [];
if ($sector) { $sql .= " WHERE sector=?"; $params[] = $sector; }
$sql .= " ORDER BY name";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as &$r) {
$r["vsm_depts"] = json_decode($r["vsm_depts"], true);
$r["kpis"] = json_decode($r["kpis"], true);
$r["routines"] = json_decode($r["routines"], true);
$r["compliance"] = json_decode($r["compliance"], true);
}
echo json_encode(["count" => count($rows), "templates" => $rows]);
break;
case "tenant-integrations":
$tenant = $_GET["tenant"] ?? "weval";
if (!empty($parts[1]) && $parts[1] === "connect" && $_SERVER["REQUEST_METHOD"] === "POST") {
$raw = json_decode(file_get_contents("php://input"), true) ?? $_POST;
$type = $raw["type"] ?? "";
$code = $raw["code"] ?? "";
$config = $raw["config"] ?? [];
$tenant_id = $raw["tenant_id"] ?? $tenant;
if (!in_array($type, ["erp","ai","industry"])) { http_response_code(400); echo json_encode(["error"=>"invalid-type"]); break; }
try {
$pdo->prepare("INSERT INTO weval.tenant_integrations (tenant_id, integration_type, integration_code, config, status) VALUES (?,?,?,?,'active') ON CONFLICT (tenant_id, integration_type, integration_code) DO UPDATE SET config=EXCLUDED.config, status='active'")
->execute([$tenant_id, $type, $code, json_encode($config)]);
audit($pdo, "integration_connect", "$tenant_id:$type:$code", ["masked"=>count($config)." keys"]);
// If industry → apply template (clone VSM depts from template)
if ($type === "industry") {
$ts = $pdo->prepare("SELECT vsm_depts FROM weval.industry_templates WHERE code=?");
$ts->execute([$code]);
$tpl = $ts->fetch(PDO::FETCH_ASSOC);
if ($tpl) {
$depts = json_decode($tpl["vsm_depts"], true) ?? [];
foreach ($depts as $d) {
$pdo->prepare("INSERT INTO weval.vsm_dept (tenant_id, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents) SELECT ?, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents FROM weval.vsm_dept WHERE tenant_id='weval' AND dept_code=? ON CONFLICT DO NOTHING")->execute([$tenant_id, $d]);
}
}
}
echo json_encode(["ok"=>true,"tenant_id"=>$tenant_id,"type"=>$type,"code"=>$code]);
} catch (Exception $e) { http_response_code(500); echo json_encode(["error"=>$e->getMessage()]); }
} else {
$stmt = $pdo->prepare("SELECT ti.tenant_id, ti.integration_type, ti.integration_code, ti.status, ti.created_at FROM weval.tenant_integrations ti WHERE ti.tenant_id=? ORDER BY created_at DESC");
$stmt->execute([$tenant]);
echo json_encode(["tenant"=>$tenant, "integrations"=>$stmt->fetchAll(PDO::FETCH_ASSOC)]);
}
break;
case "scalability":
// Return overall scalability matrix
$erp = $pdo->query("SELECT COUNT(*) FROM weval.erp_connectors")->fetchColumn();
$ai = $pdo->query("SELECT COUNT(*) FROM weval.ai_providers")->fetchColumn();
$ind = $pdo->query("SELECT COUNT(*) FROM weval.industry_templates")->fetchColumn();
$ti = $pdo->query("SELECT COUNT(*) FROM weval.tenant_integrations")->fetchColumn();
echo json_encode([
"erp_connectors_available" => intval($erp),
"ai_providers_available" => intval($ai),
"industry_templates_available" => intval($ind),
"tenant_integrations_active" => intval($ti),
"matrix" => ["ERP" => $erp, "AI" => $ai, "Industries" => $ind, "Total_combinations" => $erp * $ai * $ind]
]);
break;
default:
echo json_encode([
"service" => "WEVIA EM API",
"version" => "1.0-godmode-17avr",
"endpoints" => [
"/api/em/agents-registry?tenant=weval&dept=?",
"/api/em/vsm?tenant=weval (list) or /api/em/vsm/{dept}",
"/api/em/bpmn-routines?tenant=weval",
"/api/em/dmaic/{tenant}/{vs_id}",
"/api/em/kpi/live?tenant=weval&dept=?",
"/api/em/poc/start (POST)",
"/api/em/plans",
"/api/em/tenant/bootstrap (POST)",
"/api/em/audit?tenant=weval",
"/api/em/erp-connectors",
"/api/em/ai-providers",
"/api/em/industry-templates?sector=?",
"/api/em/tenant-integrations?tenant=X",
"POST /api/em/tenant-integrations/connect",
"/api/em/scalability"
]
]);
}