Repository navigation
feat: add external API administrator UI - #809
Open
bballdavis wants to merge 102 commits into
Open
bballdavis wants to merge 102 commits into
bballdavis wants to merge 102 commits into
Conversation
bballdavis
force-pushed
the
feature/external-api-gui
branch
from
September 8, 2026 23:23
c5febdc to
2743176
Compare
bballdavis
force-pushed
the
feature/external-api-gui
branch
from
September 8, 2026 23:36
2743176 to
fb04fad
Compare
bballdavis
force-pushed
the
feature/external-api-gui
branch
from
September 9, 2026 12:50
fb04fad to
4961ba0
Compare
dialmaster
requested changes
Sep 13, 2026
bballdavis
force-pushed
the
feature/external-api-gui
branch
2 times, most recently
from
September 19, 2026 05:05
ffca52a to
dc54292
Compare
bballdavis
marked this pull request as ready for review
September 19, 2026 05:09
Restore the downloads-directory and cookie temp-dir log lines, the x-request-id correlation header outside the external API namespace, TV-Y7's text rating fallback, and unredacted yt-dlp stderr in server logs. Drop the bare 'key' redaction path so config upgrade logs keep naming the corrected setting. None of these were needed by the API.
Dev reworked the API keys page to lead with the "Send to Youtarr" bookmarklet. Keep that: the bookmarklet steps, key list and rate limit come first, and the key-created dialog shows the draggable bookmarklet and phone shortcut details again. The external access keys section follows only when the external API is enabled. The page is split into BookmarkletKeysSection, ExternalKeysSection, CreatedKeyDialog and ConfirmKeyActionDialog to keep ApiKeysSection under the size target.
Restore the blank line the earlier edit removed so the only change in select.tsx is the isTrusted check on the synthetic mousedown opener.
The 5/30/200 per-key ceilings were repeated in the policy defaults, the fallbacks for older keys, the editor fields and the validation. Define them once in WORKLOAD_LIMITS next to normalizePolicy, and move the useApiKeys test into the sections __tests__ folder.
- Move RequestActions, RequestTarget, RequestDetailsDialog and RequestActionDialog into ExternalRequests/components/, and the misplaced hook test into hooks/__tests__/, matching the feature directory layout used elsewhere. - Rewrite RequestTarget as readable JSX with lookup tables for type labels and status colors instead of nested ternaries on one line. - A video with no known channel now reads "Unknown channel" instead of "Channel <database id>"; pending channel requests still read "New channel request". - Cancelling an approve or reject started from the details dialog goes back to the details; one started from the list goes back to the list. Every new action resets the grant choice. - Capitalize status names in the status filter, reuse one label helper for it and the status chip, and name the page size constant. - Add tests for RequestTarget, RequestActions and the cancel paths.
USAGE_GUIDE: restore the single-video note for bookmarklet keys, drop the legacy wording, and explain that the external keys section and the Requests page only appear with EXTERNAL_API_ENABLED=true, what the nav toggle does, and that the queue works without login when AUTH_ENABLED=false. CLAUDE.md: replace the thin ExternalRequests one-liner with what each file owns, add an ApiKeysSection entry, and drop the stray line that had landed above App.tsx in the frontend list.
…UTF-8 bodies With AUTH_ENABLED=false, verifyToken never sets req.authType, so the review routes' session check returned 403 for every call. Deny API keys instead, matching the key-management routes. External API POST bodies declaring a charset other than UTF-8 now get a 415 rather than a parse error or 500.
Move the external API services, their tests, and the channel grant module into a feature folder like channel/, mediaServers/, and notifications/. Give TV-Y7 its PG-level policy rank inside externalEligibility instead of changing ratingMapper, which also drives NFO output for every video.
Express routes /EXTERNAL-API/v1/... into the external router, but the app-level body parser bypass, error envelopes, degraded-database 503, and request id handling compared req.path case-sensitively. Share one isExternalApiPath check that matches the mount the way Express does.
Creating or approving a request now only records the decision and charges quota. A small in-process executor picks approved rows up outside any lock, re-checks the key's authority, and does the slow work, so no yt-dlp call, download queueing, or file unlink runs under a row lock or inside an HTTP handler: - Channel provisioning resolves the channel without enabling it and takes locks only for the final write, where a terminated channel now fails the request instead of being re-enabled. - Deletions go through deleteVideos, so the channel .m3u, playlists, and storage pause are refreshed like an in-app delete. - A storage pause or a busy video holds the request approved instead of failing it; the executor's sweep retries it. - A jobModule.onJobEnded listener closes video requests when their job ends, so ?status=completed no longer depends on who reads the list. Reads and quota checks no longer reconcile or lock anything. - A new approval_source column records whether a request was auto-approved, so the executor re-checks auto-approval only for those rows. - The download job id is passed as an option to doSpecificDownloads rather than read from the job data, and addJob rejects a preferred job id that is not a UUID.
Approving now only queues the work; the server's executor moves the request from approved to processing to a final status, and approving an approved or processing request again does nothing. - Only pending requests can be approved, so the "Retry approval" path and its locked grant checkbox are gone. - The queue refreshes itself every 5 seconds while any request on the page is approved or processing (not while the tab is hidden), without the loading spinner, and an open details dialog follows the refresh. - The details dialog says whether a request was approved automatically or by an administrator. - The approve dialog says the work runs in the background.
Creating the request service in server.js eagerly required the channel, deletion and storage modules, which broke the server startup suites.
- Video detail reads metadata only from the cached .info.json and never runs yt-dlp or writes backfills. - Raw catalog SQL uses snake_case aliases and POSITION() instead of LOCATE(). - Video list ordering and keyset cursors (now v3) tie-break on channel video id. - Single-channel lookups use Channel.findOne with the grant include.
Document approval, background execution and held requests, cached-only video detail, auto-approved channel grants, delete and idempotency responses, the work queue env vars and the approval_source column. Bump the contract fixture to version 5 for the approved create response.
A background wake after approval could sweep before the test changed state (for example revoking a grant), which made the database job fail at random on either engine. The wake scheduler is now injectable and the database tests turn it off.
bballdavis
force-pushed
the
feature/external-api-gui
branch
from
October 22, 2026 13:37
768d826 to
446058c
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dependency and scope
Depends on #810, which must be reviewed and merged before this PR. #750 is already merged. This remains the existing cumulative branch targeting
dev; the runtime head included here is9badb936aaec4a84a7403a9d75f03dd76785c4b7(verified runtime CI).The original GUI head
353d05bband its integration history are preserved. UI-only comparison against the runtime head. Once #810 lands, this PR can be synchronized withdevfor its final phase-three review.Result
Adds Settings controls for external key permissions, rating/media limits, quotas, and channel grants, plus the administrator Requests page for video, channel, and deletion requests.
The follow-up cross-boundary review found unavailable channel grants were incorrectly returned as generic 500 errors. That fix and real-database HTTP/rollback regressions were committed to #810 first, verified in CI, and then merged here.
Persistence and dependency safety
Production backend code, migrations, and dependency locks match the verified runtime branch. The four merged #750 migrations remain authoritative; obsolete markers, duplicate migration chains, channel uniqueness/deduplication, and the obsolete GUI index migration are absent. Duplicate channel rows remain intact, and no global dependency override was restored.
Validation
Browser fixtures stub startup configuration and use synthetic download/provisioning/deletion executors. No real customer data or external-service operations were used. Neither PR has been merged or deployed.
Published head:
1f20ea4ad1780a5ec94c6f24478992c7dd5ec2a9. CI: all 11 checks passed on this exact head, including both database engines and their query-plan artifacts. The existing review request is retained; no new review request is being sent while #810 remains the prerequisite.