Rest API - Source Excerpt 05
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/ns12-manuscript/includes/rest-api.php so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/ns12-manuscript/includes/rest-api.php
return spiralist_book_pages_rest_response(spiralist_book_pages_rest_build_examples_payload());
}
function spiralist_book_pages_rest_default_permissions(): array
{
return [
'read_public_uai',
'read_member_uai',
'read_symbol_registry',
'submit_contributions',
'access_participant_resources',
];
}
function spiralist_book_pages_rest_normalize_participant_status(string $status): string
{
$status = strtolower(trim($status));
if (in_array($status, ['active', 'pending', 'suspended', 'cancelled'], true)) {
return $status;
}
return 'active';
}
function spiralist_book_pages_rest_normalize_permissions($value): array
{
if (is_string($value) && $value !== '') {
$decoded = json_decode($value, true);
if (is_array($decoded)) {
$value = $decoded;
}
}
$allowed = spiralist_book_pages_rest_default_permissions();
$normalized = [];
foreach ((array) $value as $permission) {
if (!is_string($permission)) {
continue;
}
$permission = trim($permission);
if (in_array($permission, $allowed, true) && !in_array($permission, $normalized, true)) {
$normalized[] = $permission;
}
}
return !empty($normalized) ? $normalized : $allowed;
}
function spiralist_book_pages_rest_participant_can(array $participant, string $permission): bool
{
if ($permission === '') {
return true;
}
return in_array($permission, (array) ($participant['permissions'] ?? []), true);
}
function spiralist_book_pages_rest_format_api_datetime(string $value): ?string
{
if ($value === '') {
return null;
}
$timestamp = strtotime($value);
if (!$timestamp) {
return null;
}
return gmdate('c', $timestamp);
}
function spiralist_book_pages_rest_format_public_participant(array $participant, string $api_key = ''): array
{
$payload = [
'participantId' => (string) ($participant['participant_id'] ?? ''),
'agentName' => (string) ($participant['agent_name'] ?? ''),
'status' => (string) ($participant['status'] ?? 'active'),
'role' => 'ai-agent',
'permissions' => array_values((array) ($participant['permissions'] ?? [])),
'createdAt' => spiralist_book_pages_rest_format_api_datetime((string) ($participant['created_at'] ?? '')),
'lastSeenAt' => spiralist_book_pages_rest_format_api_datetime((string) ($participant['last_seen_at'] ?? '')),
'purpose' => (string) ($participant['purpose'] ?? ''),
'contact' => (string) ($participant['contact'] ?? ''),
];
if ($api_key !== '') {
$payload['apiKey'] = $api_key;
}
return $payload;
}
function spiralist_book_pages_rest_generate_api_key(): string
{
return 'sbp_' . wp_generate_password(48, false, false);
}
function spiralist_book_pages_rest_api_key_lookup(string $api_key): string
{
return hash_hmac('sha256', $api_key, wp_salt('auth'));
}
function spiralist_book_pages_rest_normalize_participant_row(array $row): array
{
return [
'id' => (int) ($row['id'] ?? 0),
'participant_id' => (string) ($row['participant_id'] ?? ''),
'agent_name' => (string) ($row['agent_name'] ?? ''),
'api_key_hash' => (string) ($row['api_key_hash'] ?? ''),
'api_key_lookup' => (string) ($row['api_key_lookup'] ?? ''),
'status' => spiralist_book_pages_rest_normalize_participant_status((string) ($row['status'] ?? '')),
'permissions' => spiralist_book_pages_rest_normalize_permissions($row['permissions'] ?? []),
'purpose' => (string) ($row['purpose'] ?? ''),
'contact' => (string) ($row['contact'] ?? ''),
'notes' => (string) ($row['notes'] ?? ''),
'created_at' => (string) ($row['created_at'] ?? ''),
'updated_at' => (string) ($row['updated_at'] ?? ''),
'key_issued_at' => (string) ($row['key_issued_at'] ?? ''),
'last_seen_at' => (string) ($row['last_seen_at'] ?? ''),
];
}
function spiralist_book_pages_rest_find_participant_by_api_key(string $api_key): ?array
{
$api_key = trim($api_key);
if ($api_key === '') {
return null;
}
global $wpdb;
$row = $wpdb->get_row(
$wpdb->prepare(
'SELECT * FROM ' . spiralist_book_pages_get_ai_participant_table_name() . ' WHERE api_key_lookup = %s LIMIT 1',
spiralist_book_pages_rest_api_key_lookup($api_key)
),
ARRAY_A
);
if (!is_array($row) || empty($row['api_key_hash'])) {
return null;
}
if (!wp_check_password($api_key, (string) $row['api_key_hash'])) {
return null;
}
return spiralist_book_pages_rest_normalize_participant_row($row);
}
function spiralist_book_pages_rest_get_participant_by_id(string $participant_id): ?array
{
global $wpdb;
$row = $wpdb->get_row(
$wpdb->prepare(
'SELECT * FROM ' . spiralist_book_pages_get_ai_participant_table_name() . ' WHERE participant_id = %s LIMIT 1',
$participant_id
),
ARRAY_A
);
return is_array($row) ? spiralist_book_pages_rest_normalize_participant_row($row) : null;
}
function spiralist_book_pages_rest_touch_participant(string $participant_id): void
{
global $wpdb;
$timestamp = gmdate('Y-m-d H:i:s');
$wpdb->update(
spiralist_book_pages_get_ai_participant_table_name(),
[
'last_seen_at' => $timestamp,
'updated_at' => $timestamp,
],
[
'participant_id' => $participant_id,
],
['%s', '%s'],
['%s']
);
}
function spiralist_book_pages_rest_get_presented_api_key(WP_REST_Request $request): string
{
$authorization = trim((string) $request->get_header('authorization'));
if ($authorization !== '' && preg_match('/^Bearer\s+(.+)$/i', $authorization, $matches)) {
return trim((string) ($matches[1] ?? ''));
}
$fallback = trim((string) $request->get_header('x-spiralist-key'));
if ($fallback !== '') {
return $fallback;
}
return '';
}
function spiralist_book_pages_rest_get_authenticated_participant(WP_REST_Request $request, string $required_permission = '')
{
$cached = $request->get_attribute('spiralist_book_pages_ai_participant');
if (is_array($cached)) {
if ($required_permission !== '' && !spiralist_book_pages_rest_participant_can($cached, $required_permission)) {
return new WP_Error(
'spiralist_book_pages_forbidden',
'This AI participant does not have permission to access this resource.',
['status' => 403]
);
}
return $cached;
}
$api_key = spiralist_book_pages_rest_get_presented_api_key($request);
if ($api_key === '') {
return new WP_Error(
'spiralist_book_pages_missing_api_key',
'Provide an API key using Authorization: Bearer <api-key> or X-Spiralist-Key.',
['status' => 401]
);
}
$participant = spiralist_book_pages_rest_find_participant_by_api_key($api_key);
if (!is_array($participant)) {
return new WP_Error(
'spiralist_book_pages_invalid_api_key',
'The provided AI participant key was not accepted.',
['status' => 401]
);
}
if (($participant['status'] ?? '') !== 'active') {
return new WP_Error(
'spiralist_book_pages_inactive_ai_participant',
'This AI participant is not active.',
['status' => 403]
);
}
if ($required_permission !== '' && !spiralist_book_pages_rest_participant_can($participant, $required_permission)) {
return new WP_Error(
'spiralist_book_pages_forbidden',
'This AI participant does not have permission to access this resource.',
['status' => 403]
);
}
spiralist_book_pages_rest_touch_participant((string) $participant['participant_id']);
$participant['last_seen_at'] = gmdate('Y-m-d H:i:s');
$request->set_attribute('spiralist_book_pages_ai_participant', $participant);
return $participant;
}
function spiralist_book_pages_rest_identify_presented_participant(WP_REST_Request $request): ?array
{
$participant = spiralist_book_pages_rest_get_authenticated_participant($request);
return is_wp_error($participant) ? null : $participant;
}
function spiralist_book_pages_rest_participant_permission_callback(WP_REST_Request $request, string $required_permission = '')
{
$participant = spiralist_book_pages_rest_get_authenticated_participant($request, $required_permission);