302 lines
15 KiB
PHP
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"
|
|
]
|
|
]);
|
|
}
|