Skip to content

feat(api): call WolfStar bot API via $api (legacy-style)#303

Draft
RedStar071 wants to merge 36 commits into
mainfrom
cursor/backport-structure-api-88be
Draft

feat(api): call WolfStar bot API via $api (legacy-style)#303
RedStar071 wants to merge 36 commits into
mainfrom
cursor/backport-structure-api-88be

Conversation

@RedStar071

@RedStar071 RedStar071 commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

Legacy-style bot API auth: the browser establishes a real sapphire SAPPHIRE_AUTH cookie via POST ${NUXT_PUBLIC_API_BASE_URL}/oauth/callback (e.g. http://localhost:8282/oauth/callback), matching the old Skyra/dashboard flow.

Auth flow

  1. Better Auth completes Discord login (dashboard session)
  2. Callback probes the bot API; if no sapphire cookie, silent Discord authorize (prompt=none) returns a fresh code
  3. Browser POSTs that code to ${apiBaseUrl}/oauth/callback with credentials: "include"SAPPHIRE_AUTH on the API origin
  4. Client $api calls the bot origin directly with credentials: "include"
  5. Sign-out also hits ${apiBaseUrl}/oauth/logout

Structure

  • Pure helpers in shared/utils/bot-oauth.ts
  • Nuxt-aware fetch helpers in app/utils/bot-oauth.ts (no fake use* composable)
  • Removed the /api/** catch-all BFF (only Nuxt auth routes remain)
  • SSR still synthesizes a sapphire cookie from the better-auth Discord token when needed

Test plan

  • pnpm lint:fix / pnpm typecheck / pnpm test:unit
  • Logged-in profile: Network shows localhost:8282/oauth/callback POST then localhost:8282/users/@me → 200 with SAPPHIRE_AUTH cookie
  • If local cookies fail, try NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8282 + bot domainOverwrite: '127.0.0.1'
Open in Web Open in Cursor 

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Confidence Score: 4/5

This PR has one contained issue to fix before merging.

The main API migration is coherent, and prior auth-scoped cache issues appear addressed. The remaining issue is a real mutation-cache bug that can skip repeated settings saves.

shared/utils/fetch-cache-config.ts; server/plugins/fetch-cache.ts

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced the mutation cache behavior with a standalone TypeScript harness, showing shouldCacheFetch returns true for the PATCH mutation and that two identical PATCH requests produce the same cache key; the first call misses the cache and hits upstream once, while the second call uses the cached response.
  • Reviewed the environment blockers and recovery steps, noting that Playwright could not start from config.webServer, recovery logs show install and Nuxt types generated, Storybook became reachable despite earlier issues, and transform errors related to Tsconfig prevented full UI rendering; blocker evidence includes a screenshot and a video.

View all artifacts

T-Rex Ran code and verified through T-Rex

Fix All in Claude Code Fix All in Cursor Fix All in Cursor Cloud Agents

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
shared/utils/fetch-cache-config.ts:157-163
**Do not cache mutations**
`shouldCacheFetch` allows every method for allowed bot hosts, and `cachedFetch` returns a stored response before calling `$fetch`. The changed `submitChanges` path now sends `PATCH /guilds/:id/settings` through `$api`, so repeating the same settings body within the TTL hits this cache and skips the bot API update entirely while the UI treats it as saved. Restrict caching to safe read methods before checking the domain.

Reviews (6): Last reviewed commit: "fix(api): keep botApi crypto helpers ser..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

cursoragent and others added 2 commits July 17, 2026 21:05
Backport the old same-origin /api/commands and /api/languages structure,
proxying to the internal bot API and normalizing alias → aliases.

Co-authored-by: RedStar <redstar071@proton.me>
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 55.20833% with 43 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.14%. Comparing base (8babfde) to head (7bfbb0d).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
shared/utils/fetch-cache-config.ts 64.00% 8 Missing and 10 partials ⚠️
server/utils/discord/index.ts 8.33% 9 Missing and 2 partials ⚠️
app/plugins/api.ts 22.22% 5 Missing and 2 partials ⚠️
server/utils/botApi.ts 77.77% 0 Missing and 4 partials ⚠️
app/plugins/payload-cache.client.ts 50.00% 2 Missing ⚠️
app/composables/useAuditLog.ts 66.66% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #303      +/-   ##
==========================================
- Coverage   71.53%   71.14%   -0.39%     
==========================================
  Files         167      169       +2     
  Lines        3302     3393      +91     
  Branches      702      730      +28     
==========================================
+ Hits         2362     2414      +52     
- Misses        447      468      +21     
- Partials      493      511      +18     
Flag Coverage Δ
component 65.93% <37.50%> (-0.17%) ⬇️
unit 67.31% <58.75%> (-0.37%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
package.json 100.00% <ø> (ø)
server/utils/runtimeConfig.ts 78.57% <ø> (ø)
app/composables/useAuditLog.ts 68.18% <66.66%> (-1.82%) ⬇️
app/plugins/payload-cache.client.ts 50.00% <50.00%> (ø)
server/utils/botApi.ts 77.77% <77.77%> (ø)
app/plugins/api.ts 50.00% <22.22%> (-30.00%) ⬇️
server/utils/discord/index.ts 34.28% <8.33%> (-1.60%) ⬇️
shared/utils/fetch-cache-config.ts 67.74% <64.00%> (-16.88%) ⬇️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 44 untouched benchmarks


Comparing cursor/backport-structure-api-88be (17e6d59) with main (6194ef0)

Open in CodSpeed

Route dashboard guild/commands/languages handlers through sapphire-plugin-api
with SAPPHIRE_AUTH cookie auth, instead of calling Discord REST locally.
Keep moderation logs, members list, users, and auth on the Nuxt server.

Co-authored-by: RedStar <redstar071@proton.me>
@cursor cursor Bot changed the title feat(api): restore commands/languages proxies to api.wolfstar.rocks feat(api): proxy full dashboard API to api.wolfstar.rocks Jul 17, 2026
Comment thread server/api/guilds/[guild]/logs/index.get.ts Outdated
Comment thread server/api/guilds/[guild]/logs/commands.get.ts Outdated
Restore the $api plugin against NUXT_PUBLIC_API_BASE_URL, point dashboard
composables at bot paths, and keep a single /api/bot/** BFF for client-side
auth cookie injection. Revert guild handlers to local Discord implementations.

Co-authored-by: RedStar <redstar071@proton.me>
@cursor cursor Bot changed the title feat(api): proxy full dashboard API to api.wolfstar.rocks feat(api): call WolfStar bot API via $api Jul 18, 2026
autofix-ci Bot and others added 3 commits July 18, 2026 11:32
Use window instead of time for defineWrappedResponseHandler rate limits
and apply lint formatting on related $api call sites.

Co-authored-by: RedStar <redstar071@proton.me>
Remove the /api/bot BFF and always point $api at NUXT_PUBLIC_API_BASE_URL
with credentials:include, matching legacy apiFetch behavior. Keep optional
SSR sapphire cookie injection from the better-auth Discord session.

Co-authored-by: RedStar <redstar071@proton.me>
@cursor cursor Bot changed the title feat(api): call WolfStar bot API via $api feat(api): call WolfStar bot API via $api (legacy-style) Jul 18, 2026
Comment thread app/plugins/api.ts
cursoragent and others added 6 commits July 18, 2026 11:52
Relocate sapphire auth crypto and BotApiCommand types to #shared so the
$api plugin can import them without a dynamic #server/utils/bot-api load.

Co-authored-by: RedStar <redstar071@proton.me>
Use camelCase botApi file and symbol names (BotApi* types,
normalizeBotApiCommands, getOptionalBotApiAuthHeaders) in shared.

Co-authored-by: RedStar <redstar071@proton.me>
Drop the redundant NUXT_BOT_API_OAUTH_SECRET alias; sapphire cookie
encryption uses the existing Discord OAuth client secret.

Co-authored-by: RedStar <redstar071@proton.me>
Add npmx-style runtime payload cache so HTML SSR can reuse a serialized
_payload.json response and skip a second full render. Auth routes are
excluded; Netlify uses Blobs storage for the payload-cache mount.

Co-authored-by: RedStar <redstar071@proton.me>
Drop the duplicate BotApiCommand type and command normalizers; type
commands fetches with the existing FlattenedCommand shape.

Co-authored-by: RedStar <redstar071@proton.me>
Include baseURL, query, and auth headers in SSR fetch-cache keys so
authenticated $api responses cannot bleed across users or filters.

Remove nested UButton/role=region markup from CommandSubCategory so
prerendered /commands passes html-validator when live bot API data loads.

Co-authored-by: RedStar <redstar071@proton.me>
@blacksmith-sh

This comment has been minimized.

`$api` plugin setup was destructuring `runtimeConfig.discord.clientSecret`,
which is undefined in the browser and crashed plugin init, causing hydration
mismatches and View Transition failures in Playwright.

Co-authored-by: RedStar <redstar071@proton.me>
Comment thread app/plugins/api.ts Outdated
cursoragent and others added 2 commits July 18, 2026 13:41
…ture-api-88be

Co-authored-by: RedStar <redstar071@proton.me>
Move node:crypto sapphire auth helpers out of #shared into
server/utils/botApi and load them via a dynamic import inside the
$api plugin's server branch so the client bundle never resolves crypto.

Co-authored-by: RedStar <redstar071@proton.me>
Comment on lines +157 to +163
export function shouldCacheFetch(url: string, options: FetchCacheKeyOptions = {}): boolean {
if (typeof url === "string" && url.startsWith("/") && !options.baseURL) {
return true;
}

try {
return isAllowedDomain(resolveFetchCacheUrl(url, options));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Do not cache mutations
shouldCacheFetch allows every method for allowed bot hosts, and cachedFetch returns a stored response before calling $fetch. The changed submitChanges path now sends PATCH /guilds/:id/settings through $api, so repeating the same settings body within the TTL hits this cache and skips the bot API update entirely while the UI treats it as saved. Restrict caching to safe read methods before checking the domain.

Artifacts

Repro: standalone mutation cache harness source

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: mutation cache harness execution log

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: shared/utils/fetch-cache-config.ts
Line: 157-163

Comment:
**Do not cache mutations**
`shouldCacheFetch` allows every method for allowed bot hosts, and `cachedFetch` returns a stored response before calling `$fetch`. The changed `submitChanges` path now sends `PATCH /guilds/:id/settings` through `$api`, so repeating the same settings body within the TTL hits this cache and skips the bot API update entirely while the UI treats it as saved. Restrict caching to safe read methods before checking the domain.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Cursor Cloud Agents

Replace Prisma-backed GuildData with hand-typed shared types, route
guild settings/logs through the WolfStar bot API, and keep audit drains
on Sentry + signed FS only.

Co-authored-by: RedStar <redstar071@proton.me>
@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Too many files changed for review. (146 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

cursoragent and others added 14 commits July 22, 2026 12:25
…ture-api-88be

Co-authored-by: RedStar <redstar071@proton.me>
…om/wolfstar-project/wolfstar.rocks into cursor/backport-structure-api-88be

Co-authored-by: RedStar <redstar071@proton.me>
autofix.ci re-added prisma allowBuilds keys after the Prisma removal;
remove them so the workspace config matches the dependency tree.

Co-authored-by: RedStar <redstar071@proton.me>
Drop unused audit member resolver, settings PATCH schema, dashboard
audit-action constant, audit log mocks, and @sapphire/ratelimits.

Co-authored-by: RedStar <redstar071@proton.me>
…om/wolfstar-project/wolfstar.rocks into cursor/backport-structure-api-88be

Co-authored-by: RedStar <redstar071@proton.me>
better-auth optionally peers prisma packages; keep their install scripts
disabled with explicit false entries so autofix stops rewriting placeholders.

Co-authored-by: RedStar <redstar071@proton.me>
Route the profile page through `$api("/users/@me")` like the legacy
dashboard, remove the Nuxt `/api/users` proxy and Discord getCurrentUser
helpers, and keep Nuxt tests on a same-origin `$api` base under process.test.

Co-authored-by: RedStar <redstar071@proton.me>
Align Storybook MSW, Lighthouse CDP mocks, and test-build defaults with
the local bot origin from .env.example so profile/guild $api calls match
the mocked host in CI and Storybook.

Co-authored-by: RedStar <redstar071@proton.me>
Client $api cannot set a cross-origin SAPPHIRE_AUTH cookie toward
localhost:8282, so authenticated browser calls 401. Route client $api
through the same-origin /api/bot proxy that injects auth from the
better-auth Discord session; keep direct bot calls on SSR.

Co-authored-by: RedStar <redstar071@proton.me>
Replace /api/bot with server/api/[...path] so client $api uses /api/**.
Remove Nuxt guild/health endpoints and their contract tests; auth routes
under /api/auth/** remain the only non-proxied API surface.

Co-authored-by: RedStar <redstar071@proton.me>
Co-authored-by: RedStar <redstar071@proton.me>
After better-auth login, bridge a Discord code to the bot API oauth/callback
(legacy Skyra flow) so the browser gets a real sapphire cookie on the API
origin, then call $api against that origin with credentials:include.

Co-authored-by: RedStar <redstar071@proton.me>
ufo encodes spaces in scope as `+`, so assert via searchParams instead of
percent-encoding.

Co-authored-by: RedStar <redstar071@proton.me>
@blacksmith-sh

This comment has been minimized.

cursoragent and others added 4 commits July 22, 2026 17:17
Drop the stateful-looking useBotOauth bag of helpers. Keep pure helpers in
shared/utils/bot-oauth and Nuxt-aware fetch helpers in app/utils/bot-oauth.

Co-authored-by: RedStar <redstar071@proton.me>
Co-authored-by: RedStar <redstar071@proton.me>
Override Better Auth's Discord default of prompt=none so first login and
new scopes show a consent screen. Drop redundant email from configured
scopes and trim bot-oauth commentary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants