Files
html/api/opus5-plan-registry.php
2026-04-17 18:25:01 +02:00

165 lines
7.7 KiB
PHP

<?php
// OPUS5 — Plan Registry (doctrine 83)
// CRUD pour les plans multi-étapes: admin.wevia_plans + admin.wevia_plan_steps
// Actions: init, create, list, get, update_status, add_step, list_steps
header('Content-Type: application/json');
$R = ['ts'=>date('c'), 'source'=>'opus5-plan-registry'];
$PG_DSN = 'pgsql:host=10.1.0.3;port=5432;dbname=adx_system;user=admin;password=admin123';
try {
$db = new PDO($PG_DSN, null, null, [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT=>5]);
} catch (Throwable $e) {
http_response_code(500); echo json_encode(['err'=>'pg_connect', 'msg'=>$e->getMessage()]); exit;
}
$raw = file_get_contents('php://input');
$d = json_decode($raw, true) ?: [];
$action = $_GET['action'] ?? ($d['action'] ?? 'list');
if ($action === 'init') {
try {
$db->exec("CREATE TABLE IF NOT EXISTS admin.wevia_plans (
plan_id VARCHAR(64) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(32) DEFAULT 'draft',
priority INT DEFAULT 0,
created_by VARCHAR(64) DEFAULT 'opus5',
created_at TIMESTAMP DEFAULT NOW(),
started_at TIMESTAMP,
finished_at TIMESTAMP,
metadata JSONB DEFAULT '{}'::jsonb
)");
$db->exec("CREATE INDEX IF NOT EXISTS idx_plans_status ON admin.wevia_plans(status)");
$db->exec("CREATE INDEX IF NOT EXISTS idx_plans_created ON admin.wevia_plans(created_at DESC)");
$db->exec("CREATE TABLE IF NOT EXISTS admin.wevia_plan_steps (
step_id SERIAL PRIMARY KEY,
plan_id VARCHAR(64) REFERENCES admin.wevia_plans(plan_id) ON DELETE CASCADE,
step_order INT NOT NULL,
step_name VARCHAR(255),
step_type VARCHAR(32) DEFAULT 'action',
action_url TEXT,
action_method VARCHAR(8) DEFAULT 'POST',
action_payload JSONB,
depends_on INT[] DEFAULT '{}'::int[],
status VARCHAR(32) DEFAULT 'pending',
result TEXT,
started_at TIMESTAMP,
finished_at TIMESTAMP,
task_id VARCHAR(64),
retry_count INT DEFAULT 0
)");
$db->exec("CREATE INDEX IF NOT EXISTS idx_steps_plan ON admin.wevia_plan_steps(plan_id, step_order)");
$db->exec("CREATE INDEX IF NOT EXISTS idx_steps_status ON admin.wevia_plan_steps(status)");
$R['tables_ready'] = ['admin.wevia_plans', 'admin.wevia_plan_steps'];
} catch (Throwable $e) { $R['err'] = $e->getMessage(); }
$R['doctrine'] = '83 — plan registry PG tables (plans + plan_steps)';
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
if ($action === 'create') {
$name = substr((string)($d['name'] ?? ''), 0, 255);
$description = (string)($d['description'] ?? '');
$priority = (int)($d['priority'] ?? 0);
$steps = $d['steps'] ?? []; // array of step definitions
if (!$name) { http_response_code(400); echo json_encode(['err'=>'no_name']); exit; }
$plan_id = 'plan_' . date('YmdHis') . '_' . bin2hex(random_bytes(3));
try {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO admin.wevia_plans (plan_id, name, description, priority, metadata) VALUES (?,?,?,?,?::jsonb)");
$stmt->execute([$plan_id, $name, $description, $priority, json_encode($d['metadata'] ?? [])]);
$step_count = 0;
foreach ($steps as $idx => $s) {
$stmt = $db->prepare("INSERT INTO admin.wevia_plan_steps (plan_id, step_order, step_name, step_type, action_url, action_method, action_payload, depends_on) VALUES (?,?,?,?,?,?,?::jsonb,?::int[])");
$depends = '{' . implode(',', array_map('intval', $s['depends_on'] ?? [])) . '}';
$stmt->execute([
$plan_id,
$idx + 1,
substr((string)($s['name'] ?? "step_" . ($idx+1)), 0, 255),
substr((string)($s['type'] ?? 'action'), 0, 32),
(string)($s['url'] ?? ''),
substr((string)($s['method'] ?? 'POST'), 0, 8),
json_encode($s['payload'] ?? []),
$depends
]);
$step_count++;
}
$db->commit();
$R['plan_id'] = $plan_id;
$R['steps_created'] = $step_count;
} catch (Throwable $e) {
if ($db->inTransaction()) $db->rollBack();
http_response_code(500); echo json_encode(['err'=>$e->getMessage()]); exit;
}
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
if ($action === 'list') {
$limit = min(50, (int)($_GET['limit'] ?? 20));
$status_filter = preg_replace('/[^a-z_]/', '', $_GET['status'] ?? '');
$sql = "SELECT plan_id, name, status, priority, created_at, finished_at, (SELECT COUNT(*) FROM admin.wevia_plan_steps WHERE plan_id=p.plan_id) AS steps_total, (SELECT COUNT(*) FROM admin.wevia_plan_steps WHERE plan_id=p.plan_id AND status='done') AS steps_done FROM admin.wevia_plans p";
if ($status_filter) $sql .= " WHERE status='$status_filter'";
$sql .= " ORDER BY created_at DESC LIMIT $limit";
$rows = $db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
$R['count'] = count($rows);
$R['plans'] = $rows;
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
if ($action === 'get') {
$pid = preg_replace('/[^a-z0-9_-]/i', '', $_GET['plan_id'] ?? $d['plan_id'] ?? '');
if (!$pid) { http_response_code(400); echo json_encode(['err'=>'no_plan_id']); exit; }
$stmt = $db->prepare("SELECT * FROM admin.wevia_plans WHERE plan_id=?");
$stmt->execute([$pid]);
$plan = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$plan) { http_response_code(404); echo json_encode(['err'=>'not_found']); exit; }
$stmt = $db->prepare("SELECT * FROM admin.wevia_plan_steps WHERE plan_id=? ORDER BY step_order");
$stmt->execute([$pid]);
$plan['steps'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$R['plan'] = $plan;
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
if ($action === 'update_status') {
$pid = preg_replace('/[^a-z0-9_-]/i', '', (string)($d['plan_id'] ?? ''));
$status = preg_replace('/[^a-z_]/', '', (string)($d['status'] ?? ''));
if (!$pid || !$status) { http_response_code(400); echo json_encode(['err'=>'missing']); exit; }
if ($status === 'running') {
$db->prepare("UPDATE admin.wevia_plans SET status=?, started_at=COALESCE(started_at, NOW()) WHERE plan_id=?")->execute([$status, $pid]);
} elseif (in_array($status, ['done','failed','cancelled'])) {
$db->prepare("UPDATE admin.wevia_plans SET status=?, finished_at=NOW() WHERE plan_id=?")->execute([$status, $pid]);
} else {
$db->prepare("UPDATE admin.wevia_plans SET status=? WHERE plan_id=?")->execute([$status, $pid]);
}
$R['updated'] = $pid;
$R['new_status'] = $status;
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
if ($action === 'update_step') {
$step_id = (int)($d['step_id'] ?? 0);
$status = preg_replace('/[^a-z_]/', '', (string)($d['status'] ?? ''));
$result = (string)($d['result'] ?? '');
if (!$step_id) { http_response_code(400); echo json_encode(['err'=>'no_step_id']); exit; }
if ($status === 'running') {
$db->prepare("UPDATE admin.wevia_plan_steps SET status=?, started_at=NOW() WHERE step_id=?")->execute([$status, $step_id]);
} elseif (in_array($status, ['done','failed'])) {
$db->prepare("UPDATE admin.wevia_plan_steps SET status=?, result=?, finished_at=NOW() WHERE step_id=?")->execute([$status, substr($result,0,5000), $step_id]);
}
$R['step_updated'] = $step_id;
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
http_response_code(400);
echo json_encode(['err'=>'unknown_action', 'available'=>['init','create','list','get','update_status','update_step']]);