Skip to content

Add admin-togglable feature flags, gate AI monster generator - #360

Open
HarmlessHarm wants to merge 9 commits into
developfrom
feature/feature-flags-admin
Open

Add admin-togglable feature flags, gate AI monster generator#360
HarmlessHarm wants to merge 9 commits into
developfrom
feature/feature-flags-admin

Conversation

@HarmlessHarm

@HarmlessHarm HarmlessHarm commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a small, code-defined feature flag registry (src/utils/featureFlags.js) backed by Firebase (feature_flags/<flag_id>), with a Vuex module (src/store/modules/featureFlags.js) that fetches current values once per app load — client boot and every SSR render, not live-reactive to already-open tabs, and no redeploy required for a toggle to take effect.
  • New admin page at /admin/feature-flags (src/views/Admin/FeatureFlags.vue) listing every registered flag with a toggle, gated the same way as the rest of /admin.
  • First flag, monster_generator (default: enabled), wired up in two places:
    • Client: hides the "Generate from description" entry point in the New Monster dialog (EditNpc.vue) when disabled.
    • Server (the actual kill switch): POST /ai/generate-monster in src-ssr/api/index.js now rejects with a 403 when disabled, before any credit lookup or call to the external generator API.
  • Firebase security rules for feature_flags (admin-only write, public read) have been added, matching the existing promotions rule.
  • Full spec/design/task breakdown: openspec/changes/feature-flags-admin/

Test plan

  • npm run lint passes
  • npm run ssr — client and server compile cleanly; / returns 200; /admin/feature-flags 302-redirects when not signed in as admin
  • Sign in as admin, confirm /admin/feature-flags lists monster_generator toggled on by default
  • Toggle monster_generator off, reload the New Monster dialog in EditNpc.vue, confirm "Generate from description" is gone
  • With the flag off, call POST /ai/generate-monster directly with a valid token and confirm it's rejected without spending credits or calling the external API
  • Toggle back on, reload, confirm both the entry point and endpoint work as before
  • Remove the feature_flags/monster_generator node entirely and confirm the feature behaves as enabled (registry default)

🤖 Generated with Claude Code

https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR

HarmlessHarm and others added 9 commits September 11, 2026 20:48
Adds a small, code-defined feature flag registry backed by Firebase,
an admin page to toggle flags, and a Vuex module that fetches current
values once per app load (client boot / each SSR render) — no live
push to open tabs, no redeploy required for a toggle to take effect.

Wires up the first flag, monster_generator, in two places: hides the
"Generate from description" entry point in the New Monster dialog when
disabled, and rejects POST /ai/generate-monster server-side so the
kill switch is authoritative even if a request bypasses the UI.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR
q-list wasn't passed the dark prop, so q-item-label text (the flag id
caption) rendered with light-theme coloring on the dark background.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR
- EditNpc.vue: hide the "OR" divider along with the "Generate from
  description" button so it doesn't dangle when the flag is off.
- Npcs.vue: the NPC list page has its own "Generate" toolbar button
  and overflow-menu item that also open the AI generator; gate both.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR
ImportContent/index.vue has its own "Generate" button opening
GenerateMonster.vue, separate from the NPC list and New Monster
dialog. Verified via grep that all GenerateMonster.vue usages
(3 total) are now gated by the monster_generator flag.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR
All 27 tasks confirmed complete.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR
src-ssr/*.js is not run through Babel (see the warning comment in
src-ssr/index.js), and this project's webpack 4 parser doesn't
understand the ES2020 optional-chaining/nullish-coalescing syntax
used in isFlagEnabled's fallback lookup. Dev mode (npm run ssr) didn't
catch this since it takes a different, more lenient path; the
production build (npx quasar build -m ssr, used by the Dockerfile)
failed to parse it.

Replaced with a plain flagDefault() helper using only pre-ES2020
syntax. Verified with a full `npx quasar build -m ssr` run.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AuRuGWkBbBzeKpqtrq9qR
@sonarqubecloud

Copy link
Copy Markdown

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