Files
kane-diagnostics/hubzilla/addon/cry01/cry01_spool.php
2026-06-08 02:08:51 -04:00

146 lines
5.7 KiB
PHP

<?php
/**
* cry01_spool.php — POST handlers.
* Validates input, builds spool envelope, POSTs to orchestrator.
* Does not render HTML — returns rendered output from cry01_renderer.php.
*/
// ---------------------------------------------------------------------------
// SIGNAL POST HANDLER
// ---------------------------------------------------------------------------
function cry01_handle_signal_post($association_slug, $access) {
// Validates and submits a capacity signal to the orchestrator spool receiver.
$g1_pubkey = trim($_POST['g1_pubkey'] ?? '');
$direction = trim($_POST['direction'] ?? '');
$category_id = trim($_POST['category_id'] ?? '');
$capacity_description = trim($_POST['capacity_description'] ?? '');
$g1_denomination = trim($_POST['g1_denomination'] ?? '');
$duration_days = intval($_POST['duration_days'] ?? 0);
// Validate required fields.
$errors = [];
if (!$g1_pubkey) $errors[] = 'Ğ1 public key is required.';
if (!in_array($direction, ['offer', 'seek'])) $errors[] = 'Direction must be offer or seek.';
if (!$category_id) $errors[] = 'Category is required.';
if (!$capacity_description) $errors[] = 'Description is required.';
if (!$g1_denomination || !is_numeric($g1_denomination) || floatval($g1_denomination) <= 0) {
$errors[] = 'Ğ1 amount must be a positive number.';
}
if ($duration_days < 1 || $duration_days > 365) {
$errors[] = 'Duration must be between 1 and 365 days.';
}
if ($errors) {
return '<div class="cry01-content"><div class="alert alert-danger"><ul class="mb-0">'
. implode('', array_map(fn($e) => '<li>' . cry01_h($e) . '</li>', $errors))
. '</ul></div></div>'
. cry01_render_signal_form($association_slug, $access);
}
$config = cry01_load_config();
$token = $config['node_token'] ?? '';
// Build the spool envelope per contracts/signal-v1.json.
$envelope = [
'_header' => [
'addon' => 'cry01',
'association_slug' => $association_slug,
'participant_xchan' => get_observer_hash(),
'submitted_at' => date('c'),
'node_token_hash' => hash('sha256', $token),
],
'_payload' => [
'g1_pubkey' => $g1_pubkey,
'capacity_description' => $capacity_description,
'g1_denomination' => $g1_denomination,
'duration_days' => $duration_days,
'category_id' => $category_id,
'direction' => $direction,
],
];
$result = cry01_post_to_orchestrator('/cry01/signal', $envelope);
if ($result === true) {
return '<div class="cry01-content">'
. '<div class="alert alert-success">Signal registered successfully. '
. 'A Verifiable Credential will be issued and timestamped to Bitcoin.</div>'
. '<p><a href="' . z_root() . '/cry01/' . cry01_h($association_slug) . '">&larr; Back to Value Layer</a></p>'
. '</div>';
}
return '<div class="cry01-content">'
. '<div class="alert alert-danger">Signal could not be registered. '
. 'The orchestrator may be temporarily unavailable. Please try again.</div>'
. '</div>'
. cry01_render_signal_form($association_slug, $access);
}
// ---------------------------------------------------------------------------
// MANAGE POST HANDLER
// ---------------------------------------------------------------------------
function cry01_handle_manage_post($association_slug) {
// Handles operator manage actions: currently only cache refresh.
if (!cry01_verify_csrf()) {
return cry01_render_error('Invalid form token. Please reload and try again.');
}
$action = $_POST['action'] ?? '';
if ($action === 'refresh_cache') {
$ok = cry01_refresh_balance_cache();
$msg = $ok
? '<div class="alert alert-success">Balance cache refreshed successfully.</div>'
: '<div class="alert alert-warning">Cache refresh failed. Check that the Duniter node is reachable and the operator Ğ1 key is configured.</div>';
return '<div class="cry01-content">' . $msg . '</div>' . cry01_render_manage($association_slug);
}
return cry01_render_error('Unknown action.');
}
// ---------------------------------------------------------------------------
// ORCHESTRATOR TRANSPORT
// ---------------------------------------------------------------------------
function cry01_post_to_orchestrator($path, $payload) {
// POSTs a JSON envelope to the orchestrator spool receiver.
// Returns true on success (HTTP 201), false on any failure.
$config = cry01_load_config();
$base = rtrim($config['receiver_url'] ?? '', '/');
if (!$base) {
logger('cry01_spool: receiver_url not configured');
return false;
}
$url = $base . $path;
$body = json_encode($payload);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nContent-Length: " . strlen($body) . "\r\n",
'content' => $body,
'timeout' => 10,
'ignore_errors' => true,
],
]);
$raw = @file_get_contents($url, false, $context);
$headers = $http_response_header ?? [];
$status = 0;
foreach ($headers as $h) {
if (preg_match('/HTTP\/\d\.\d\s+(\d+)/', $h, $m)) {
$status = intval($m[1]);
}
}
if ($status === 201) return true;
logger('cry01_spool: orchestrator returned status ' . $status . ' for ' . $path);
return false;
}