feat(web): add authenticated Community (Let's Chat) button for members #315

Merged
longjacksonle merged 6 commits from feat/BUNYIP-329-community-button into main 2026-07-02 00:15:12 +02:00

What

BUNYIP-329: surface Let's Chat as an authenticated "Community" button in the Bunyip front end. Members get a dashboard button that drops them into the team Let's Chat instance already logged in, with no separate sign-in.

How it authenticates

Let's Chat is already registered as an OIDC client of bunyip-api (migration 20260618032217_register_lets_chat_oidc_client.sql, callback https://chat.a8n.systems/auth/bunyip/callback). So this is the same single-sign-in bridge the app tiles use: opening the instance runs its OIDC client against bunyip-api, and the member's existing OP session logs them in. No token minting in bunyip-web.

Changes

  • Config (BUNYIP_COMMUNITY_URL): the full URL the Community button opens, empty by default. Empty disables the feature (button hidden, /community sends the user back to the dashboard) so a deploy without Let's Chat never shows a dead link. Point it at the login-init path (e.g. https://chat.a8n.systems/auth/bunyip) so the OIDC flow fires immediately instead of landing on Let's Chat's own login page.
  • GET /community: guards the session, then redirects a member into the configured instance; a non-member is sent to the membership upsell. The routing decision is a pure community_redirect_target (member+configured -> Let's Chat; non-member -> /membership; unconfigured -> /dashboard).
  • Dashboard card: a member-only "Community" card ("Join the community and chat with us") with an "Open Community" button that opens /community in a new tab. Hidden for non-members and when unconfigured.

Tests

cargo fmt --all --check, cargo clippy -p bunyip-web --all-targets (clean), and cargo test -p bunyip-web (52 passing) all green. New unit tests cover the three redirect branches (member into Let's Chat, non-member upsell, unconfigured fallback), which is the authenticated-redirect behavior AC3 asks for.

Assumptions / scope

  • Single shared, team-managed instance for the initial rollout (per the ticket). Channels/structure are out of scope.
  • The exact Let's Chat login-init path is deferred to BUNYIP_COMMUNITY_URL config rather than hardcoded, since it is an ops/infra detail; the /auth/bunyip value follows the Passport callback convention in the existing migration.

Closes BUNYIP-329.

## What BUNYIP-329: surface Let's Chat as an authenticated "Community" button in the Bunyip front end. Members get a dashboard button that drops them into the team Let's Chat instance already logged in, with no separate sign-in. ## How it authenticates Let's Chat is already registered as an OIDC client of bunyip-api (migration `20260618032217_register_lets_chat_oidc_client.sql`, callback `https://chat.a8n.systems/auth/bunyip/callback`). So this is the same single-sign-in bridge the app tiles use: opening the instance runs its OIDC client against bunyip-api, and the member's existing OP session logs them in. No token minting in bunyip-web. ## Changes - **Config** (`BUNYIP_COMMUNITY_URL`): the full URL the Community button opens, empty by default. Empty disables the feature (button hidden, `/community` sends the user back to the dashboard) so a deploy without Let's Chat never shows a dead link. Point it at the login-init path (e.g. `https://chat.a8n.systems/auth/bunyip`) so the OIDC flow fires immediately instead of landing on Let's Chat's own login page. - **`GET /community`**: guards the session, then redirects a member into the configured instance; a non-member is sent to the membership upsell. The routing decision is a pure `community_redirect_target` (member+configured -> Let's Chat; non-member -> `/membership`; unconfigured -> `/dashboard`). - **Dashboard card**: a member-only "Community" card ("Join the community and chat with us") with an "Open Community" button that opens `/community` in a new tab. Hidden for non-members and when unconfigured. ## Tests `cargo fmt --all --check`, `cargo clippy -p bunyip-web --all-targets` (clean), and `cargo test -p bunyip-web` (52 passing) all green. New unit tests cover the three redirect branches (member into Let's Chat, non-member upsell, unconfigured fallback), which is the authenticated-redirect behavior AC3 asks for. ## Assumptions / scope - Single shared, team-managed instance for the initial rollout (per the ticket). Channels/structure are out of scope. - The exact Let's Chat login-init path is deferred to `BUNYIP_COMMUNITY_URL` config rather than hardcoded, since it is an ops/infra detail; the `/auth/bunyip` value follows the Passport callback convention in the existing migration. Closes BUNYIP-329.
BUNYIP-329 groundwork. Add a `community_url` field to the bunyip-web Config, read from `BUNYIP_COMMUNITY_URL` (empty by default), plus a `community_enabled()` helper. Empty disables the Community feature so a deploy without a Let's Chat instance never renders a dead button. Let's Chat is already an OIDC client of bunyip-api (migration 20260618032217), so opening this URL authenticates the member through their existing OP session; the value should point at the login-init path (e.g. https://chat.a8n.systems/auth/bunyip) so the OIDC bridge fires immediately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BUNYIP-329 (AC2/AC3). Add a GET /community handler that guards the session and redirects a member into the configured Let's Chat instance, where Let's Chat's OIDC client + the member's existing bunyip-api OP session log them in with no separate login (the same bridge the app tiles rely on). The routing decision is factored into a pure `community_redirect_target`: member + configured URL -> Let's Chat; non-member -> membership upsell; feature unconfigured -> back to the dashboard (no dead external link). Unit tests cover all three branches. Also backfills the new config field in the security.rs test Config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BUNYIP-329 (AC1). Render a "Community" card on the dashboard for members when a Let's Chat instance is configured (is_member && community_enabled), with the copy "Join the community and chat with us" and an "Open Community" button that opens /community in a new tab. The card is hidden for non-members and when the feature is unconfigured, so it never advertises a destination the user cannot reach.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
style: elide needless lifetime on community_redirect_target
Some checks failed
E2E / Playwright against deployment (pull_request) Successful in 35s
Check / fmt + clippy + build + tests (pull_request) Has been cancelled
00eaf225e2
Clippy needless_lifetimes: the explicit 'a is redundant now that the only
reference parameter drives the output lifetime; the static fallback literals
coerce fine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(compose): pass BUNYIP_COMMUNITY_URL through to bunyip-web
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 1m3s
Check / fmt + clippy + build + tests (pull_request) Successful in 8m51s
1f1a29296d
BUNYIP-329: wire the new env var into the web service in both the dev and production compose files (empty default = feature off), so an operator can point the Community button at the Let's Chat instance via .env without editing compose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(web): move Community from a dashboard card to the left sidebar nav
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 23s
Check / fmt + clippy + build + tests (pull_request) Successful in 8m54s
Create release / Create release from merged PR (pull_request) Has been skipped
a8eadc82d2
BUNYIP-329 UI change (David's request): surface Community front and centre in the left nav instead of as a dashboard card. Add a "Community" entry to the dashboard sidebar, placed second (right under Dashboard), gated on membership + a configured Let's Chat instance. Membership is read from the user already available in `dashboard_shell` and threaded into `sidebar`/`dashboard_items`; the config flag rides a `COMMUNITY_ENABLED` OnceLock installed once in main (same pattern as SSE_API_ORIGIN), so no `dashboard_response` call site changes. The nav link opens in a new tab (it launches the external instance). The dashboard card is removed. The `/community` redirect handler and its tests are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-07-02 00:11:33 +02:00
longjacksonle deleted branch feat/BUNYIP-329-community-button 2026-07-02 00:15:13 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!315
No description provided.