diff --git a/hubzilla/addon/vs01/vs01_spool.php b/hubzilla/addon/vs01/vs01_spool.php index 877eb9d..5a50b2a 100644 --- a/hubzilla/addon/vs01/vs01_spool.php +++ b/hubzilla/addon/vs01/vs01_spool.php @@ -7,9 +7,9 @@ * See VS-RENDERER-SPEC.md — Spool POST Handler section. */ -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // POST HANDLER -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- function vs01_handle_post($association_slug, $vs_code, $access) { $schemas = vs01_load_schemas(); @@ -62,30 +62,29 @@ function vs01_handle_post($association_slug, $vs_code, $access) { $out .= '
  • ' . vs01_h($e) . '
  • '; } $out .= ''; - // Re-render form with submitted values and errors $out .= vs01_render_vs_form_with_values($association_slug, $vs_code, $access, $fields); return $out; } - // Build spool envelope - $config = vs01_load_config(); - $assoc = $config['associations'][$association_slug] ?? []; - $envelope = vs01_build_spool_envelope( + // Build and POST spool envelope + $config = vs01_load_config(); + $assoc = $config['associations'][$association_slug] ?? []; + $envelope = vs01_build_spool_envelope( $vs_code, $perspective_key, $fields, $association_slug, - $assoc['channel_id'] ?? '' + $assoc['channel_id'] ?? '', + $access, + $config['node_token'] ?? '' ); - // POST to orchestrator - $result = vs01_post_to_orchestrator($envelope); + $result = vs01_post_to_orchestrator($envelope, $config); if ($result === true) { - // THROWAWAY — replace with TMP review redirect or confirmation page return '
    - Your record for ' . vs01_h($vs_code) . ' has been submitted. + Your record for ' . vs01_h($vs_code) . ' has been saved. Return to ' . vs01_h($assoc['name'] ?? $association_slug) . ' @@ -99,9 +98,9 @@ function vs01_handle_post($association_slug, $vs_code, $access) {
    '; } -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // VALIDATION -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- function vs01_validate_required($perspective, $fields) { $errors = []; @@ -116,32 +115,65 @@ function vs01_validate_required($perspective, $fields) { return $errors; } -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // SPOOL ENVELOPE -// ---------------------------------------------------------------------------- +// Matches contracts/vs01/record-v1.json — _header / _payload shape. +// --------------------------------------------------------------------------- -function vs01_build_spool_envelope($vs_code, $perspective, $fields, $association_slug, $channel_id) { +function vs01_build_spool_envelope($vs_code, $perspective, $fields, $association_slug, $channel_id, $standing, $node_token) { return [ - 'addon' => 'vs01', - 'contract_version' => '1.0', - 'vs_code' => $vs_code, - 'perspective' => $perspective, - 'association_slug' => $association_slug, - 'association_channel_id' => (string) $channel_id, - 'submitted_at' => date('c'), - 'standing' => vs01_access_state($association_slug), - 'fields' => $fields, + '_header' => [ + 'addon' => 'vs01', + 'contract_version' => '1.0', + 'association_slug' => $association_slug, + 'association_channel_id' => (string) $channel_id, + 'participant_id' => vs01_get_participant_id($association_slug), + 'submitted_at' => date('c'), + 'standing' => $standing, + 'node_token_hash' => hash('sha256', $node_token), + ], + '_payload' => [ + 'vs_code' => $vs_code, + 'perspective' => $perspective, + 'fields' => $fields, + ], ]; } -// ---------------------------------------------------------------------------- -// ORCHESTRATOR POST -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// PARTICIPANT ID — Placekey derived from SASE channel address +// --------------------------------------------------------------------------- -function vs01_post_to_orchestrator($envelope) { - $config = vs01_load_config(); +function vs01_get_participant_id($association_slug) { + // The participant_id is the Placekey — the SASE-verified channel address + // for this participant, which encodes their postal address. + // It is stored in the channel's xchan_addr field. + // Format: SASE1-{encoded-address} + $observer = get_observer_hash(); + if (!$observer) return ''; + + $r = q("SELECT xchan_addr FROM xchan WHERE xchan_hash = '%s' LIMIT 1", + dbesc($observer) + ); + if (!$r) return ''; + + // xchan_addr is in the form "channelname@hostname" + // The channel name IS the Placekey for SASE participants. + $addr = $r[0]['xchan_addr'] ?? ''; + $parts = explode('@', $addr); + return $parts[0] ?? ''; +} + +// --------------------------------------------------------------------------- +// ORCHESTRATOR POST +// --------------------------------------------------------------------------- + +function vs01_post_to_orchestrator($envelope, $config = null) { + if ($config === null) { + $config = vs01_load_config(); + } $receiver_url = $config['receiver_url'] ?? ''; - $node_token = $config['node_token'] ?? ''; + $node_token = $config['node_token'] ?? ''; if (!$receiver_url) { logger('vs01_spool: receiver_url not configured'); @@ -166,8 +198,8 @@ function vs01_post_to_orchestrator($envelope) { ], ]); - $response = curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $response = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_error($ch); curl_close($ch); @@ -184,9 +216,9 @@ function vs01_post_to_orchestrator($envelope) { return 'Orchestrator error (HTTP ' . $http_code . ').'; } -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // RE-RENDER FORM WITH SUBMITTED VALUES -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- function vs01_render_vs_form_with_values($association_slug, $vs_code, $access, $submitted_values) { $schemas = vs01_load_schemas();