Skip to content

API v2 Redux#2645

Draft
jnovack wants to merge 10 commits into
FalconChristmas:masterfrom
jnovack:api-v2
Draft

API v2 Redux#2645
jnovack wants to merge 10 commits into
FalconChristmas:masterfrom
jnovack:api-v2

Conversation

@jnovack

@jnovack jnovack commented May 16, 2026

Copy link
Copy Markdown
Member

PR: Consolidate v1/v2 API into single router

The SHORT SHORT Version

  • Backward compatibility with all existing v1 routes is fully preserved.
  • Adds /api/v2 routes (currently only some verb changes)
  • Restructures tools, creates helpers, and enumerates documentation for multi version management.
  • Playwright testing has been initially created, serves as a template of what's possible, and can be expanded upon.

WWWAPI-GUIDELINES.md outlines the details of why this change is so wordy, as all the files were re-written to comply with the formatting, style and structure to maintain consistency between files (there's still more work to do...)

What changed

www/api/index.php

  • Added $apiVersionPrefixes = ['', '/v2'] array and dispatch_all(string $path, string $method, string $fn) helper that registers a route under every version prefix in one call.
  • Added require_once 'controllers/helpers.php' for the new getJsonBody() utility.
  • Renamed all 52 v1 dispatch callback strings from snake_case / mixed-case to PascalCase (public functions) and camelCase (helpers), matching the merged controllers. No behavioral change.
  • Converted all routes that are identical between v1 and v2 from individual dispatch_get(...) calls to dispatch_all(...) calls.
  • Added 25 dispatch_post('/v2/...') lines for routes that changed from GET to POST in v2 (all state-changing operations: start/stop/pause/resume playlist, sequence control, fppd restart/start/stop, system reboot/shutdown, git reset, event trigger, script run, file move/onUpload, network interface add).
  • Renamed the remoteAction GET handler to RemoteAction_v1 (query-param format). Added dispatch_post('/v2/remoteAction', 'RemoteAction') for the JSON-body v2 variant.
  • Updated addPluginEndpoints() to call dispatch_all() so plugins are automatically registered under both /plugin/... and /v2/plugin/....

www/api/controllers/ (16 files replaced)

Replaced each v1 controller with its v2 counterpart. All changes are naming convention only — no behavioral differences:

  • Public API functions: snake_casePascalCase
  • Internal helper functions: mixed/PascalCase → camelCase

Every controller function's docblock now uses version-tagged route annotations instead of a single @route tag:

  • @route-v1 METHOD /path and @route-v2 METHOD /path declare per-version routes (paths are prefix-free; the generator prepends the server base URL).
  • @badge-v1 "DEPRECATED" warning marks v1-only GET variants as deprecated in the generated OpenAPI spec.
  • @body-vN, @response-vN, and @deprecated-vN are also supported for version-specific overrides.

Affected files: cape, network, system, sequence, backups, playlist, settings, channel, configfile, files, events, scripts, git, plugin, audioaliases, pipewire

www/api/controllers/helpers.php (new)

Moved from www/api/v2/controllers/helpers.php. Contains getJsonBody(bool $required = true): ?array, a centralized JSON request-body parser used by POST handlers that previously called file_get_contents('php://input') ad hoc.

www/api/controllers/proxies.php

Added RemoteAction_v1() shim that reads ip and action from $_GET query parameters (v1 GET behavior). The existing RemoteAction() function reads from the JSON request body (v2 POST behavior).

www/api/controllers/help.php and www/apihelp.php (removed)

The legacy help controller and its front-end entry point have been deleted. API documentation is now served exclusively from the OpenAPI spec.

Spec/doc file reorganization

Moved v1 static files into a www/api/v1/ subdirectory to match the v2 layout:

  • www/api/openapi.jsonwww/api/v1/openapi.json
  • www/api/api.htmlwww/api/v1/api.html

OpenAPI tooling (www/api/tools/)

  • generate_openapi.py renamed to generate_openapi_v1.py.
  • New generate_openapi_base.py — shared generator logic imported by both generate_openapi_v1.py and generate_openapi_v2.py. Parses the @route-vN / @badge-vN / @deprecated-vN docblock tags and builds version-filtered specs from a single controller source.
  • convert_endpoints.py deleted (one-off migration tool, no longer needed).

Test infrastructure

  • tests/playwright/fixtures/version.ts (new): exports V1 = '/api', V2 = '/api/v2', and testBothVerbVersions() helper. For routes where only the verb changed (identical logic), one assertion function is shared by two thin @v1 / @v2 tagged test wrappers. Routes where behavior changed (e.g. remoteAction) keep separate test functions.
  • tests/playwright/tests/api-v2/*.spec.ts: verb-changed routes updated to use testBothVerbVersions(); individual tests carry @covered tags for coverage tracking.
  • tests/playwright/scripts/check-api-coverage.ts: router path updated from www/api/v2/index.php to www/api/index.php; filter updated to only match /v2/ prefixed routes when diffing against API_COVERAGE.md.
  • tests/playwright/package.json: added test:api:v1 and test:api:v2 scripts.
  • Plenty of tags to test @e2e, @security, @full, @schema...

Backward compatibility

All v1 routes continue to respond at their original paths and with their original HTTP verbs. No v1 client is broken (hopefully...)

Fixed

ID Severity File Route Title
#API-RTE-1 🔴 Critical v2/index.php v2/sequence/{SequenceName} DELETE route points at a non-existent callback
#FIL-VERB-3 🟠 High v2/controllers/files.php v2/file/move/{fileName}, v2/file/onUpload/{ext}/** File-moving and upload-trigger endpoints are exposed as GET
#NET-VERB-2 🟠 High v2/controllers/network.php v2/network/interface/add/{interface} Network interface creation is exposed as GET
#PLG-VERB-2 🟠 High v2/controllers/plugin.php v2/plugin/{RepoName}/upgrade Plugin upgrade still accepts GET for a mutating operation
#SYS-VERB-1 🟠 High v2/controllers/system.php v2/system/reboot, v2/system/shutdown, v2/system/fppd/* Device-control operations are exposed as GET
#PLS-VERB-1 🟠 High v2/controllers/playlist.php v2/playlists/*, v2/playlist/{PlaylistName}/start* Playlist start/stop/pause operations use GET instead of a mutating verb
#EVT-VERB-1 🟠 High v2/controllers/events.php v2/events/{eventId}/trigger Event triggering is exposed as GET
#PRX-VERB-2 🟠 High v2/controllers/proxies.php v2/remoteAction Remote control actions are exposed through a GET query endpoint
#SCR-VERB-1 🟠 High v2/controllers/scripts.php v2/scripts/{scriptName}/run, v2/scripts/installRemote/{category}/{filename} Script execution and installation are exposed as GET
#GIT-VERB-1 🟠 High v2/controllers/git.php v2/git/reset Destructive git reset is exposed as GET
#SEQ-VERB-1 🟠 High v2/controllers/sequence.php v2/sequence/{SequenceName}/start/{startSecond}, v2/sequence/current/* Sequence control endpoints mutate player state over GET

@jnovack jnovack requested a review from OnlineDynamic May 16, 2026 14:42
@jnovack jnovack force-pushed the api-v2 branch 2 times, most recently from 00353bb to 777994e Compare May 16, 2026 22:19
@OnlineDynamic OnlineDynamic requested a review from dkulp May 17, 2026 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant