Plugin Directory

Changeset 3483307


Ignore:
Timestamp:
03/15/2026 09:21:36 PM (2 weeks ago)
Author:
mateuszflowsystems
Message:

Release 1.3.0

Location:
flowsystems-webhook-actions
Files:
7 added
2 deleted
12 edited

Legend:

Unmodified
Added
Removed
  • flowsystems-webhook-actions/trunk/README.txt

    r3477178 r3483307  
    11=== Flow Systems Webhook Actions ===
    22Contributors: mateuszflowsystems
    3 Tags: webhook, automation, integration, n8n, api
     3Tags: webhooks, automation, integration, n8n, api
    44Requires at least: 6.0
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.2.1
     7Stable tag: 1.3.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
     
    3434- Replace fragile custom `wp_remote_post()` integrations
    3535- Build idempotent WordPress automation pipelines
     36- Query delivery logs, trigger retries, or manage webhooks programmatically from CI/CD pipelines or external dashboards using API tokens
     37- Allow AI coding assistants (e.g. Claude Code) to inspect webhook logs and retry failed events automatically
     38- Use AI agents to monitor webhook delivery health and operate the queue through the REST API
    3639
    3740= Event Identity & Idempotency =
     
    113116Payloads always include stable event metadata for consistency.
    114117
     118= REST API Access with Token Authentication =
     119
     120The plugin exposes a full operational REST API (`/wp-json/fswa/v1/`) that powers the admin interface and can also be used directly by external tools, automation systems, AI agents, and CI/CD pipelines.
     121
     122Every endpoint supports dual authentication:
     123
     124- WordPress admin session (cookie-based, used by the admin panel)
     125- API token — for programmatic access without a browser session
     126
     127**API Tokens**
     128
     129Create tokens directly from the API Tokens screen in the admin panel. Each token is assigned one of three scopes:
     130
     131- `read` — GET access to webhooks, logs, queue, health, triggers, and schemas
     132- `operational` — Read + toggle webhooks on/off, retry and replay log entries, execute queue jobs
     133- `full` — Operational + create, update, and delete webhooks, schemas, and queue jobs
     134
     135Token authentication is accepted via:
     136
     137- `X-FSWA-Token: <token>` header (recommended)
     138- `Authorization: Bearer <token>` header
     139- `?api_token=<token>` query parameter
     140
     141Tokens can be set to expire and rotated at any time. Rotation issues a new secret immediately while preserving the token's name, scope, and settings. Token management always requires a WordPress admin login — tokens cannot be used to create or manage other tokens.
     142
     143Full REST API documentation: https://flowsystems.pl/webhook-wordpress-plugin-api/
     144
     145= AI Agents and Programmatic Automation =
     146
     147The REST API makes Flow Systems Webhook Actions accessible to AI-powered tools and coding agents.
     148
     149Automation systems, CI pipelines, and AI coding assistants (such as Claude Code or Cursor) can safely interact with webhook infrastructure using API tokens without requiring WordPress admin sessions.
     150
     151Typical AI-driven workflows include:
     152
     153- AI agents monitoring webhook delivery health
     154- Automatically retrying failed webhook events
     155- Inspecting delivery logs to debug integrations
     156- Enabling or disabling webhooks dynamically during deployments
     157- Managing automation pipelines across environments
     158
     159Because the API exposes operational endpoints for logs, queue jobs, webhooks, and triggers, external agents can treat WordPress as a programmable event infrastructure.
     160
     161Example scenarios:
     162
     163• A Claude Code agent analyzes webhook delivery logs and automatically retries failed integrations.
     164• A CI/CD pipeline disables webhook triggers during deployments and re-enables them afterward.
     165• Automation systems query webhook health metrics and alert when the queue becomes stuck.
     166• External dashboards display real-time webhook delivery metrics using API tokens.
     167
     168This allows WordPress automation pipelines to be controlled entirely through HTTP APIs, enabling advanced integration with AI-driven development workflows.
     169
    115170= Developer Friendly =
    116171
    117172- Works with any WordPress or WooCommerce action
    118 - Internal REST endpoints power the admin interface
     173- Full REST API (`/wp-json/fswa/v1/`) usable from any HTTP client — not just the admin panel
     174- API token authentication with scoped access (`read`, `operational`, `full`)
    119175- Fully extensible via filters and actions
    120176- Clean namespace and unique prefixes
     
    134190- Event UUIDs and timestamps
    135191- Full delivery logging and metrics
     192- REST API with token authentication for programmatic access
    136193
    137194Built for developers who need production-grade automation reliability.
     
    193250Yes. For user-related triggers (such as `user_register`, `profile_update`, `wp_login`, `wp_logout`), you can enable "Include User Data" to automatically add user information (ID, email, display name, roles, etc.) to the payload.
    194251
     252= Can I access the REST API without a WordPress login? =
     253
     254Yes. Create an API token from the API Tokens screen in the admin panel and use it in the `X-FSWA-Token` header (or `Authorization: Bearer`) with your requests. Tokens support three scopes — `read`, `operational`, and `full` — so you can grant only the access level each integration needs.
     255
    195256= Is this plugin free? =
    196257
     
    2062676. Queue status overview
    2072687. Settings configuration screen
     2698. REST API Tokens configuration screen
    208270
    209271== Changelog ==
     272
     273= 1.3.0 — 2026-03-15 =
     274- Added API token authentication for the REST API — create tokens with `read`, `operational`, or `full` scope; tokens are SHA-256 hashed at rest and accepted via `X-FSWA-Token` header, `Authorization: Bearer`, or `?api_token=` query param
     275- Added token expiry support with optional `expires_at`; expired tokens are rejected at auth time and visually flagged in the admin panel
     276- Added token rotation — issues a new secret while preserving all other token fields; optionally updates expiry in the same request; revived expired tokens auto-extend to +30 days by default
     277- Added `PATCH /tokens/{id}` endpoint for updating `expires_at` independently of rotation
     278- Added `fswa_api_tokens` database table (migration 1.3.0)
     279- Applied scope-based dual auth (`manage_options` session OR valid token) to all existing REST controllers: `read` for GET endpoints, `operational` for toggle/retry/replay, `full` for create/update/delete
     280- Fixed all admin UI date displays (logs, queue, schema panel) to show times in the user's local timezone instead of raw UTC
     281- Fixed date range filters (logs, queue) to correctly convert local picker values to UTC before querying
     282- Improved log details panel — error message, response body, HTTP code, and duration now reflect the most recent attempt history entry rather than the top-level log fields
    210283
    211284= 1.2.1 — 2026-03-07 =
     
    255328== Upgrade Notice ==
    256329
     330= 1.3.0 =
     331Adds a new database table for API tokens. The table is created automatically on update — no manual steps needed.
     332
    257333= 1.1.1 =
    258334Fixes permanently_failed entries being undercounted in delivery statistics. No database changes.
  • flowsystems-webhook-actions/trunk/admin/dist/.vite/manifest.json

    r3477165 r3483307  
    11{
    22  "src/main.js": {
    3     "file": "assets/main-oBfATGcf.js",
     3    "file": "assets/main-C1bVA2d5.js",
    44    "name": "main",
    55    "src": "src/main.js",
     
    77  },
    88  "style.css": {
    9     "file": "assets/style-BckjMVPE.css",
     9    "file": "assets/style-BgO0BuI6.css",
    1010    "src": "style.css"
    1111  }
  • flowsystems-webhook-actions/trunk/flowsystems-webhook-actions.php

    r3477178 r3483307  
    44 * Plugin URI: https://flowsystems.pl/wordpress-webhook-actions
    55 * Description: Trigger HTTP webhooks from WordPress actions (do_action). Easily connect WordPress with n8n, Zapier, Make, or custom workflows.
    6  * Version: 1.2.1
     6 * Version: 1.3.0
    77 * Author: Mateusz Skorupa
    88 * Author URI: https://flowsystems.pl
     
    1717defined('ABSPATH') || exit;
    1818
    19 define('FSWA_VERSION', '1.2.1');
     19define('FSWA_VERSION', '1.3.0');
    2020define('FSWA_FILE', __FILE__);
    2121
  • flowsystems-webhook-actions/trunk/src/Activation.php

    r3477165 r3483307  
    2222
    2323    $charsetCollate = $wpdb->get_charset_collate();
    24     $webhooksTable = $wpdb->prefix . 'fswa_webhooks';
    25     $triggersTable = $wpdb->prefix . 'fswa_webhook_triggers';
    26     $logsTable     = $wpdb->prefix . 'fswa_logs';
    27     $queueTable    = $wpdb->prefix . 'fswa_queue';
    28     $statsTable    = $wpdb->prefix . 'fswa_stats';
     24    $webhooksTable  = $wpdb->prefix . 'fswa_webhooks';
     25    $triggersTable  = $wpdb->prefix . 'fswa_webhook_triggers';
     26    $logsTable      = $wpdb->prefix . 'fswa_logs';
     27    $queueTable     = $wpdb->prefix . 'fswa_queue';
     28    $statsTable     = $wpdb->prefix . 'fswa_stats';
     29    $apiTokensTable = $wpdb->prefix . 'fswa_api_tokens';
    2930
    3031    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
     
    127128    dbDelta($sqlStats);
    128129
    129     update_option('fswa_db_version', '1.2.0');
     130    // API tokens table
     131    $sqlApiTokens = "CREATE TABLE {$apiTokensTable} (
     132            id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
     133            name         VARCHAR(255) NOT NULL,
     134            token_hash   VARCHAR(64) NOT NULL,
     135            token_hint   VARCHAR(13) NOT NULL,
     136            scope        VARCHAR(20) NOT NULL DEFAULT 'read',
     137            expires_at   DATETIME DEFAULT NULL,
     138            last_used_at DATETIME DEFAULT NULL,
     139            rotated_at   DATETIME DEFAULT NULL,
     140            created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
     141            PRIMARY KEY (id),
     142            UNIQUE KEY idx_token_hash (token_hash),
     143            KEY idx_expires (expires_at)
     144        ) {$charsetCollate};";
     145
     146    dbDelta($sqlApiTokens);
     147
     148    update_option('fswa_db_version', '1.3.0');
    130149  }
    131150
     
    176195    // Drop tables (order matters for foreign key constraints)
    177196    // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     197    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fswa_api_tokens");
    178198    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fswa_stats");
    179199    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fswa_queue");
  • flowsystems-webhook-actions/trunk/src/Api/HealthController.php

    r3477165 r3483307  
    1313use FlowSystems\WebhookActions\Services\QueueService;
    1414use FlowSystems\WebhookActions\Services\StatsService;
     15use FlowSystems\WebhookActions\Api\AuthHelper;
     16use WP_Error;
    1517
    1618class HealthController extends WP_REST_Controller {
     
    4547  }
    4648
    47   /**
    48    * Check permissions
    49    */
    50   public function permissionsCheck($request): bool {
    51     return current_user_can('manage_options');
     49  public function permissionsCheck($request): bool|WP_Error {
     50    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
    5251  }
    5352
  • flowsystems-webhook-actions/trunk/src/Api/LogsController.php

    r3477165 r3483307  
    1515use FlowSystems\WebhookActions\Repositories\WebhookRepository;
    1616use FlowSystems\WebhookActions\Services\QueueService;
     17use FlowSystems\WebhookActions\Api\AuthHelper;
    1718
    1819class LogsController extends WP_REST_Controller {
     
    7980        'methods' => WP_REST_Server::CREATABLE,
    8081        'callback' => [$this, 'retryItem'],
    81         'permission_callback' => [$this, 'getItemPermissionsCheck'],
     82        'permission_callback' => [$this, 'operationalPermissionsCheck'],
    8283      ],
    8384    ]);
     
    8788      'methods'             => WP_REST_Server::CREATABLE,
    8889      'callback'            => [$this, 'replayItem'],
    89       'permission_callback' => [$this, 'getItemPermissionsCheck'],
     90      'permission_callback' => [$this, 'operationalPermissionsCheck'],
    9091    ]]);
    9192
     
    9596        'methods' => WP_REST_Server::CREATABLE,
    9697        'callback' => [$this, 'bulkRetry'],
    97         'permission_callback' => [$this, 'getItemsPermissionsCheck'],
     98        'permission_callback' => [$this, 'operationalPermissionsCheck'],
    9899        'args' => [
    99100          'ids' => [
     
    137138  }
    138139
    139   /**
    140    * Check permissions for getting items
    141    */
    142   public function getItemsPermissionsCheck($request): bool {
    143     return current_user_can('manage_options');
    144   }
    145 
    146   /**
    147    * Check permissions for getting single item
    148    */
    149   public function getItemPermissionsCheck($request): bool {
    150     return current_user_can('manage_options');
    151   }
    152 
    153   /**
    154    * Check permissions for deleting items
    155    */
    156   public function deleteItemsPermissionsCheck($request): bool {
    157     return current_user_can('manage_options');
    158   }
    159 
    160   /**
    161    * Check permissions for deleting single item
    162    */
    163   public function deleteItemPermissionsCheck($request): bool {
    164     return current_user_can('manage_options');
     140  public function getItemsPermissionsCheck($request): bool|WP_Error {
     141    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
     142  }
     143
     144  public function getItemPermissionsCheck($request): bool|WP_Error {
     145    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
     146  }
     147
     148  public function operationalPermissionsCheck($request): bool|WP_Error {
     149    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_OPERATIONAL);
     150  }
     151
     152  public function deleteItemsPermissionsCheck($request): bool|WP_Error {
     153    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
     154  }
     155
     156  public function deleteItemPermissionsCheck($request): bool|WP_Error {
     157    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
    165158  }
    166159
  • flowsystems-webhook-actions/trunk/src/Api/QueueController.php

    r3477165 r3483307  
    1414use FlowSystems\WebhookActions\Services\WPHttpTransport;
    1515use FlowSystems\WebhookActions\Repositories\WebhookRepository;
     16use FlowSystems\WebhookActions\Api\AuthHelper;
    1617
    1718class QueueController extends WP_REST_Controller {
     
    3132        'methods' => WP_REST_Server::READABLE,
    3233        'callback' => [$this, 'getItems'],
    33         'permission_callback' => [$this, 'permissionsCheck'],
     34        'permission_callback' => [$this, 'readPermissionsCheck'],
    3435        'args' => [
    3536          'status' => [
     
    7273        'methods' => WP_REST_Server::READABLE,
    7374        'callback' => [$this, 'getStats'],
    74         'permission_callback' => [$this, 'permissionsCheck'],
     75        'permission_callback' => [$this, 'readPermissionsCheck'],
    7576      ],
    7677    ]);
     
    8182        'methods' => WP_REST_Server::CREATABLE,
    8283        'callback' => [$this, 'executeItem'],
    83         'permission_callback' => [$this, 'permissionsCheck'],
     84        'permission_callback' => [$this, 'operationalPermissionsCheck'],
    8485        'args' => [
    8586          'id' => [
     
    9697        'methods' => WP_REST_Server::CREATABLE,
    9798        'callback' => [$this, 'deleteItem'],
    98         'permission_callback' => [$this, 'permissionsCheck'],
     99        'permission_callback' => [$this, 'fullPermissionsCheck'],
    99100        'args' => [
    100101          'id' => [
     
    111112        'methods' => WP_REST_Server::CREATABLE,
    112113        'callback' => [$this, 'retryItem'],
    113         'permission_callback' => [$this, 'permissionsCheck'],
     114        'permission_callback' => [$this, 'operationalPermissionsCheck'],
    114115        'args' => [
    115116          'id' => [
     
    122123  }
    123124
    124   public function permissionsCheck(WP_REST_Request $_request): bool {
    125     return current_user_can('manage_options');
     125  public function readPermissionsCheck(WP_REST_Request $request): bool|WP_Error {
     126    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
     127  }
     128
     129  public function operationalPermissionsCheck(WP_REST_Request $request): bool|WP_Error {
     130    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_OPERATIONAL);
     131  }
     132
     133  public function fullPermissionsCheck(WP_REST_Request $request): bool|WP_Error {
     134    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
    126135  }
    127136
  • flowsystems-webhook-actions/trunk/src/Api/SchemasController.php

    r3462891 r3483307  
    1313use FlowSystems\WebhookActions\Repositories\WebhookRepository;
    1414use FlowSystems\WebhookActions\Services\PayloadTransformer;
     15use FlowSystems\WebhookActions\Api\AuthHelper;
    1516
    1617class SchemasController extends WP_REST_Controller {
     
    3738        'methods' => WP_REST_Server::READABLE,
    3839        'callback' => [$this, 'getWebhookSchemas'],
    39         'permission_callback' => [$this, 'permissionsCheck'],
     40        'permission_callback' => [$this, 'readPermissionsCheck'],
    4041        'args' => [
    4142          'id' => [
     
    5354        'methods' => WP_REST_Server::READABLE,
    5455        'callback' => [$this, 'getSchema'],
    55         'permission_callback' => [$this, 'permissionsCheck'],
     56        'permission_callback' => [$this, 'readPermissionsCheck'],
    5657        'args' => [
    5758          'id' => [
     
    7071        'methods' => WP_REST_Server::EDITABLE,
    7172        'callback' => [$this, 'updateSchema'],
    72         'permission_callback' => [$this, 'permissionsCheck'],
     73        'permission_callback' => [$this, 'fullPermissionsCheck'],
    7374        'args' => [
    7475          'id' => [
     
    9596        'methods' => WP_REST_Server::DELETABLE,
    9697        'callback' => [$this, 'deleteSchema'],
    97         'permission_callback' => [$this, 'permissionsCheck'],
     98        'permission_callback' => [$this, 'fullPermissionsCheck'],
    9899        'args' => [
    99100          'id' => [
     
    116117        'methods' => WP_REST_Server::CREATABLE,
    117118        'callback' => [$this, 'resetCapture'],
    118         'permission_callback' => [$this, 'permissionsCheck'],
     119        'permission_callback' => [$this, 'fullPermissionsCheck'],
    119120        'args' => [
    120121          'id' => [
     
    137138        'methods' => WP_REST_Server::READABLE,
    138139        'callback' => [$this, 'getUserTriggers'],
    139         'permission_callback' => [$this, 'permissionsCheck'],
    140       ],
    141     ]);
    142   }
    143 
    144   /**
    145    * Permission check
    146    */
    147   public function permissionsCheck($request): bool {
    148     return current_user_can('manage_options');
     140        'permission_callback' => [$this, 'readPermissionsCheck'],
     141      ],
     142    ]);
     143  }
     144
     145  public function readPermissionsCheck($request): bool|WP_Error {
     146    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
     147  }
     148
     149  public function fullPermissionsCheck($request): bool|WP_Error {
     150    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
    149151  }
    150152
  • flowsystems-webhook-actions/trunk/src/Api/TriggersController.php

    r3464666 r3483307  
    88use WP_REST_Server;
    99use WP_REST_Response;
     10use WP_Error;
     11use FlowSystems\WebhookActions\Api\AuthHelper;
    1012
    1113class TriggersController extends WP_REST_Controller {
     
    2426  }
    2527
    26   /**
    27    * Check permissions
    28    */
    29   public function getItemsPermissionsCheck($request): bool {
    30     return current_user_can('manage_options');
     28  public function getItemsPermissionsCheck($request): bool|WP_Error {
     29    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
    3130  }
    3231
  • flowsystems-webhook-actions/trunk/src/Api/WebhooksController.php

    r3462891 r3483307  
    1111use WP_Error;
    1212use FlowSystems\WebhookActions\Repositories\WebhookRepository;
     13use FlowSystems\WebhookActions\Api\AuthHelper;
    1314
    1415class WebhooksController extends WP_REST_Controller {
     
    7172      'methods' => WP_REST_Server::CREATABLE,
    7273      'callback' => [$this, 'toggleItem'],
    73       'permission_callback' => [$this, 'updateItemPermissionsCheck'],
     74      'permission_callback' => [$this, 'toggleItemPermissionsCheck'],
    7475      'args' => [
    7576        'id' => [
     
    8182  }
    8283
    83   /**
    84    * Check permissions for getting items
    85    */
    86   public function getItemsPermissionsCheck($request): bool {
    87     return current_user_can('manage_options');
    88   }
    89 
    90   /**
    91    * Check permissions for getting single item
    92    */
    93   public function getItemPermissionsCheck($request): bool {
    94     return current_user_can('manage_options');
    95   }
    96 
    97   /**
    98    * Check permissions for creating items
    99    */
    100   public function createItemPermissionsCheck($request): bool {
    101     return current_user_can('manage_options');
    102   }
    103 
    104   /**
    105    * Check permissions for updating items
    106    */
    107   public function updateItemPermissionsCheck($request): bool {
    108     return current_user_can('manage_options');
    109   }
    110 
    111   /**
    112    * Check permissions for deleting items
    113    */
    114   public function deleteItemPermissionsCheck($request): bool {
    115     return current_user_can('manage_options');
     84  public function getItemsPermissionsCheck($request): bool|WP_Error {
     85    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
     86  }
     87
     88  public function getItemPermissionsCheck($request): bool|WP_Error {
     89    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_READ);
     90  }
     91
     92  public function createItemPermissionsCheck($request): bool|WP_Error {
     93    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
     94  }
     95
     96  public function updateItemPermissionsCheck($request): bool|WP_Error {
     97    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
     98  }
     99
     100  public function toggleItemPermissionsCheck($request): bool|WP_Error {
     101    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_OPERATIONAL);
     102  }
     103
     104  public function deleteItemPermissionsCheck($request): bool|WP_Error {
     105    return AuthHelper::dualAuth($request, AuthHelper::SCOPE_FULL);
    116106  }
    117107
  • flowsystems-webhook-actions/trunk/src/Controllers/AdminController.php

    r3462891 r3483307  
    1313use FlowSystems\WebhookActions\Api\HealthController;
    1414use FlowSystems\WebhookActions\Api\SchemasController;
     15use FlowSystems\WebhookActions\Api\ApiTokensController;
    1516
    1617class AdminController {
     
    163164    (new HealthController())->registerRoutes();
    164165    (new SchemasController())->registerRoutes();
     166    (new ApiTokensController())->registerRoutes();
    165167  }
    166168
  • flowsystems-webhook-actions/trunk/src/Database/Migrator.php

    r3477165 r3483307  
    55class Migrator {
    66  private const OPTION_KEY = 'fswa_db_version';
    7   private const CURRENT_VERSION = '1.2.0';
     7  private const CURRENT_VERSION = '1.3.0';
    88
    99  /**
     
    4545      $wpdb->prefix . 'fswa_trigger_schemas',
    4646      $wpdb->prefix . 'fswa_stats',
     47      $wpdb->prefix . 'fswa_api_tokens',
    4748    ];
    4849
     
    6869      '1.1.0' => [self::class, 'migration_1_1_0'],
    6970      '1.2.0' => [self::class, 'migration_1_2_0'],
     71      '1.3.0' => [self::class, 'migration_1_3_0'],
    7072    ];
    7173  }
     
    265267
    266268  /**
     269   * Migration 1.3.0 - Add API tokens table
     270   */
     271  public static function migration_1_3_0(): void {
     272    global $wpdb;
     273
     274    $charsetCollate = $wpdb->get_charset_collate();
     275    $tokensTable    = $wpdb->prefix . 'fswa_api_tokens';
     276
     277    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
     278
     279    $sql = "CREATE TABLE {$tokensTable} (
     280            id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
     281            name         VARCHAR(255) NOT NULL,
     282            token_hash   VARCHAR(64) NOT NULL,
     283            token_hint   VARCHAR(13) NOT NULL,
     284            scope        VARCHAR(20) NOT NULL DEFAULT 'read',
     285            expires_at   DATETIME DEFAULT NULL,
     286            last_used_at DATETIME DEFAULT NULL,
     287            rotated_at   DATETIME DEFAULT NULL,
     288            created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
     289            PRIMARY KEY (id),
     290            UNIQUE KEY idx_token_hash (token_hash),
     291            KEY idx_expires (expires_at)
     292        ) {$charsetCollate};";
     293
     294    dbDelta($sql);
     295  }
     296
     297  /**
    267298   * Get current database version
    268299   */
Note: See TracChangeset for help on using the changeset viewer.