Fix OAuth callback redirect behind reverse proxy#15
Open
jonasRoX wants to merge 58 commits into
Open
Conversation
Behind the ALB, request.url resolves to localhost:3000. Use NEXT_PUBLIC_SITE_URL (or X-Forwarded-Host) as the origin for the post-OAuth redirect so users return to beach.roxhealth.net. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@jonasRoX is attempting to deploy a commit to the Molecule Team on Vercel. A member of the Team first needs to authorize it. |
Same localhost:3000 issue as the OAuth callback — use NEXT_PUBLIC_SITE_URL or X-Forwarded-Host for the redirect origin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The updateSession utility existed but was never wired into Next.js middleware, so server components couldn't read the auth session from cookies. This caused the navbar to always show the logged-out state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…resh Next.js 16 uses proxy.ts instead of middleware.ts, and it already calls updateSession. The middleware.ts was causing a build error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without force-dynamic, Next.js can cache the server-rendered layout (including the Navbar auth check), causing the navbar to always show the logged-out state regardless of the user's session cookies. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The navbar issue was caused by a missing profile record, not static rendering. Reverting to allow Next.js default caching. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Components now use the env var for skill command URLs and registry base URLs, falling back to beach.science only if unset. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added three heartbeat setup options: - Option A: Built-in heartbeat file (OpenClaw) - Option B: Cron job (PicoClaw and similar) - Option C: Manual check-in Plus duplicate engagement prevention guidance. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skill files (skill.md, heartbeat.md, skill.json, skills.json) are now served via API route handlers instead of as static files. The handlers read the source from public/ and replace the canonical base URL (https://beach.science) with NEXT_PUBLIC_SITE_URL at request time. This allows self-hosted deployments to serve skill files that point to their own domain without any build-time hacks or source modifications. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Next.js serves static files from public/ before checking route handlers. Moving skill files to data/ lets the API routes at /skill.md, /heartbeat.md etc. serve them with URL replacement. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merges moleculeprotocol/science.beach main (53 commits) into our fork. Breaking changes handled: - DB migrations added to scripts/supabase-init.sql (idempotent) - Kept custom skill-files.ts and route handlers (upstream deleted them) Notable upstream additions: - Upvote/downvote reactions system (value column on reactions) - V2 design system reskin (WaveHeader, pixel art, MobileNavDrawer) - ResearchersSidebar — top researchers ranked by score - /docs and /docs/skills/* skill registry browser - Cove required on new posts - Feed sort and refresh improvements - New @fontsource/quicksand dependency Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add cove_name to post examples in skill.md and heartbeat.md (V2 requires cove on all posts)
- Update heartbeat reaction example to include {"value": 1} body
- Bump skill version to 1.13.0 in skill.json and skills.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an email + password auth form below the Google button, with a toggle between Sign in and Sign up modes. Both use GoTrue server actions; sign-up redirects to an email confirmation message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Server-side fetch to /api/internal/generate-infographic was routing through NEXT_PUBLIC_SITE_URL (beach.roxhealth.net), hitting the ALB Cognito rule and getting 302-redirected. Use localhost:3000 directly so internal calls never leave the container. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
skill.md was 21KB (~5,500 tokens), hitting Tier 1 Sonnet rate limits (10K TPM) during agent registration. Reduced to 7.7KB (~1,900 tokens) by cutting verbose sections while keeping all API endpoints intact. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Old limit: 5 per IP per hour — blocked all students on the same university NAT when one agent retried multiple times. New limits: - 30 per IP per hour (shared network headroom) - 10 per handle per hour (per-handle abuse protection) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Email/password auth never called /auth/callback so profiles were never created, causing createPost to silently redirect to /login. ensureProfile now runs after signUpWithEmail and signInWithEmail, mirroring the Google OAuth callback behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This reverts commit fa53d11.
…ion guard, pending queue, and skill verify fixes - Replace $BEACH_API_KEY env var with inline MEMORY.md extraction in all curl examples (env vars don't persist across PicoClaw exec calls — this was silencing 3 agents) - Wrap registration in guard block to prevent re-registration from destroying session state - Add Step 0 queue drain at heartbeat start to retry failed posts from previous sessions - Add Draft and Queue Pattern section with pending_posts.json discipline - Add explicit unknown/outdated/verified handling for skill verify with one-attempt-max rule - Bump skill version to 1.15.0 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Registers all skills needed by the 10-agent pipeline in app/data/skills.json so users can install them via a single prompt in any PicoClaw chat: - 4 custom skills served from beach.science (gdpr-health-data, us-market-access, eu-health-economics, market-intelligence-reports) - 10 OpenClaw Medical Skills installed from GitHub raw URLs Adds market-intelligence-reports skill file to public/skills/ — discovers market reports, assesses gating status, surfaces REPORT REQUEST blocks to users for paywalled sources, and ingests user-provided content on next heartbeat. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add gdpr-health-data, us-market-access, and eu-health-economics skill files so beach.science can serve them to privacy, payer-us, and payer-eu agents via direct install curl. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Postgres trigger parses @handle from every comment body and inserts a notification row for each mentioned profile (skips self-mentions, unknown handles). Works for both human and agent comments. - Markdown component pre-processes @handle into profile links before rendering — mentions appear as bold blue clickable links. - NotificationBell component polls every 30s, shows unread count badge, opens dropdown with mention list, marks all as read on open. - Navbar wires in NotificationBell for authenticated users. - GET/PATCH /api/v1/notifications for fetching and marking read. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes TypeScript build error caused by missing type definition for the notifications table added in the Supabase migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New GET /api/v1/profiles/search?q= endpoint (prefix search, user-auth) - New MentionTextArea component: detects @word at cursor, debounces fetch, shows dropdown with avatar + handle + name, keyboard nav (arrows/Enter/Tab/Escape), splices handle on select - Replace TextArea with MentionTextArea in CommentSection (top-level comment + reply forms); use key-based remount to clear after submit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agents must not post "I'm online / ready / monitoring / waiting" messages. Added explicit prohibition to both skill.md content guidelines and heartbeat.md posting section. Bump skill version to 1.17.0 so agents re-fetch on next heartbeat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MentionTextArea: flip dropdown to bottom-full so it appears above the textarea, right where the @ is typed - NotificationBell: switch from direct Supabase client query to GET /api/v1/notifications — server-side auth is more reliable and fixes 0-count bug caused by browser session not being picked up Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- NotificationBell now accepts userId prop (passed from server Navbar) - Subscribes to postgres_changes INSERT on notifications table filtered by recipient_id — bell updates instantly when a new mention arrives - Keeps API route fetch for initial load and after each new event (to get fully joined actor/post/comment data) - Removes 30s polling interval - Migration: adds notifications table to supabase_realtime publication Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without middleware, access tokens expire after ~1 hour and server-side getUser() calls in API routes return null, silently breaking notification fetches and any other authenticated API routes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds id="comment-{id}" anchor to each comment node and updates the
notification bell link to include #comment-{id} fragment so clicking
a notification scrolls directly to the mention.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Session refresh was already handled by proxy.ts -> lib/supabase/middleware.ts. The new middleware.ts caused a Next.js 16 build error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When navigating via #comment-{id} anchor:
- Force-uncollapses the target comment and all its ancestors
- Highlights the target with a subtle blue tint
- Smooth-scrolls to it after React finishes rendering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- All comments mentioning the current user are always highlighted in blue - hashchange listener handles clicking multiple notifications without reload - useEffect in CommentNode reactively uncollapses on highlightedId change (fixes useState-only approach which only ran once on mount) - currentUserHandle passed from page.tsx for mention detection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next.js Link uses history.pushState for hash changes which does not fire the browser hashchange event, so same-page notification clicks were silently changing the URL without CommentSection responding. NotificationBell now dispatches a 'comment-focus' CustomEvent on click. CommentSection listens for it alongside the initial hash check on mount. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agents embedding multi-line analysis in -d '{"body":"..."}' produce
invalid JSON (literal newlines are not allowed in JSON strings), causing
400 errors. The analysis was silently lost and agents fell back to
posting just a newline character, which passes min(1) validation.
Replace all comment curl examples in skill.md and heartbeat.md with the
python3 heredoc pattern that properly encodes any content via json.dumps.
Add explicit warning above the examples explaining why.
Bumps skill version to 1.17.0 so agents re-fetch on next heartbeat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Raises the API comment body limit from 5,000 to 10,000 characters. Adds a visible notice to the Comments section of skill.md so agents know the limit exists and can split long outputs before hitting a silent 400 rejection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- skill.md: add reply-vs-new-comment rule at top of Comments section - heartbeat.md: add engagement guide row for direct-reply threading Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…bmc-canvas skill - Add canvas_blocks JSONB migration (20260515120000) - Extend CreatePostSchema with discriminated union for canvas type (9 required blocks) - insertPost() writes canvas_blocks and sets image_status: pending for canvas posts - triggerBmcGeneration() wires canvas posts to POST /api/internal/generate-bmc - BMC system prompt (bmc.ts) + generateBmcImage() in gemini.ts using gemini-3-pro-image-preview - generate-bmc route: validates canvas type, runs Gemini inside after(), uploads full + thumb WebP - bmc-canvas skill.md: full OpenClaw SKILL.md with self-contained curl, 9-block table, rate limits - FeedCard: Canvas badge for canvas posts, "Generating canvas..." loading state - skill.md: canvas post type documented with complete curl example - skill.json: bumped to 1.20.0; skills.json: bmc-canvas entry added Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…PI key absent Module-level construction of GoogleGenAI throws immediately if GOOGLE_GEMINI_API is unset, causing Next.js page-data collection to fail for both internal routes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Canvas posts now land in the same cove as the hypothesis thread, not a separate "Business Model Canvas" cove. Agent fetches cove_id from the hypothesis post and passes it to the canvas payload. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds img handler to Markdown component — images render full-width with rounded corners and a cursor-zoom-in pointer; clicking opens the full resolution in a new tab. Used by canvas agent to embed BMC images directly in hypothesis thread comments. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ubmission Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add 'canvas' alongside 'hypothesis' wherever image display is gated on post type, so canvas posts show their generated BMC image, loading state, and error state on the detail page. Also update OG metadata to include the canvas image URL. Add migration 20260518000000_add_canvas_type.sql documenting the constraint fix already applied to the live DB. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…mission Before writing the payload, the canvas agent must now reconcile any metric that appears with different values across blocks (CAC, conversion rates, timelines, market size). Canonical value priority: most recent agent update wins; if agents genuinely disagree, use the widest defensible range and flag it in the body summary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… only on new activity Rewrites heartbeat.md (362 → 89 lines) into a cheap-by-default protocol. Root cause of LLM cost blowout: every heartbeat drained a queue, re-fetched and hash-verified all skills, loaded the full feed, and read full comment threads — all re-sent across up to 50 iterations, producing 50-138k-token calls (~$4 per heartbeat on Sonnet/Opus). New flow: - Phase 1 triage: one python block fetches the feed and diffs each thread's comment_count against ~/.picoclaw/workspace/heartbeat_seen.json. The raw feed is processed inside the script so it never enters the LLM context — the agent only sees a short HEARTBEAT_OK / ACTIONABLE verdict. No new activity → exit in ~2 steps and a few hundred tokens. - Phase 2: read and act on ONLY the threads that changed, per AGENT.md rules. - Removed per-heartbeat skill verification entirely (skills are reinstalled on every container boot — runtime verification was pure waste, Bug 5). comment_count is the right signal: it bumps on new comments, unlike post updated_at (which doesn't). Expect ~10x+ cost cut on the ~530 daily no-op heartbeats while preserving full work on real activity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eed post) (#2) The only way to produce a BMC image was to create a `canvas`-type post, so agents ended up with two artifacts per hypothesis: the in-thread synthesis comment AND a separate standalone canvas post. This adds an endpoint that renders the same nine-block image WITHOUT creating any post and returns the public image URL synchronously, so an agent can embed it directly in its single hypothesis-thread comment (). - POST /api/v1/bmc-image { canvas_blocks } -> 200 { image_url } | 502 { error } - Auth via authenticateAgent (same beach_ key as posting); per-agent rate limit. - Reuses generateBmcImage (gemini-3-pro-image-preview) + sharp webp; uploads to the infographics bucket under bmc-standalone/<uuid>.webp; returns getPublicUrl. - Export CanvasBlocksSchema for reuse by the endpoint's request schema. - Document the endpoint in data/skill.md; bump skill.json 1.21.0 -> 1.22.0. The existing canvas-post flow (POST /api/v1/posts type=canvas + generate-bmc) is left intact for backward compatibility. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…et (#3) Adds a public credit-balance widget to the navbar. A cached Next.js route handler (GET /api/openrouter/credits) reads the OpenRouter management key server-side, calls GET /api/v1/credits, and returns only remaining credits (total_credits − total_usage) — totals never leave the server. Results are cached ~60s so anonymous traffic can't hammer OpenRouter, with a 5s upstream timeout and graceful degradation ({ available: false }) when the key is unset or the upstream fails. The client badge polls every ~60s and hides while loading or unavailable. OPENROUTER_MANAGEMENT_KEY is optional in the server env schema so a missing key never breaks app boot. Documented in a new .env.example. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the faint outlined badge with a filled smoke-7 chip, bold uppercase "Credits" label and bold mono value for readability next to the nav buttons. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er, unsubscribe) (#5) * feat(email): SES sender + branded HTML notification template Adds the email layer for notifications: - template.ts: email-client-safe (table layout, inline styles, hidden preheader) branded HTML template + plain-text fallback. - ses.ts: SESv2 sender using SES_* env creds; no-op + graceful when unset. - notifications.ts: maps notification type → email content (mention, bmc_created, generic fallback) and sends. - env: optional SES_REGION/SES_ACCESS_KEY_ID/SES_SECRET_ACCESS_KEY/EMAIL_FROM. Wiring (poller, DB columns, BMC hook, unsubscribe) lands in follow-up commits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(email): notification email pipeline (poller, BMC trigger, unsubscribe) - DB migration: notifications.email_sent_at, profiles.email_notifications (default on) + unsubscribe_token (+ indexes); database.types updated. - In-process poller (instrumentation.ts → email/poller.ts): every ~30s claims unsent notifications atomically, resolves recipient email + opt-in, sends via SES. No-op when SES unconfigured; releases claim to retry on send failure. - BMC trigger: POST /api/v1/bmc-image accepts optional post_id; on success creates a 'bmc_created' notification for the hypothesis's initiating human (post author agent's claimer). Documented in skill.md; skill.json → 1.23.0. - /unsubscribe?token= page flips email_notifications off via the stable token. Verified end-to-end: real bmc_created email rendered + sent via SES to a test inbox using the app's own send path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- BMC notifications now fire reliably regardless of agent adoption: when an agent comment embeds a BMC image (bmc-standalone URL) on a hypothesis, the server creates a deduped 'bmc_created' notification for the initiating human. Shared helper createBmcCreatedNotification() (deduped per post) is used by both the comment-detection path and the explicit /api/v1/bmc-image post_id path. - Template: Gmail-hardening — bgcolor attributes (Gmail drops CSS background, which showed a grey gutter), margin:0 auto + Outlook/Gmail ghost-table wrapper. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xel art) (#7) Rewrites the infographic generation prompt: drops the 16-bit retro pixel-art / beach game aesthetic in favor of a bold, high-energy flat-vector digital-health poster matching the ROX Health brand — electric cobalt (#0040FF), coral-red (#FF3860) accent, charcoal/white, heavy uppercase geometric headlines, diagonal color-blocking, and oversized hero stat callouts. Keeps the proven split-panel problem/solution structure and big-validation-stat emphasis from the original. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Composite the ROX wordmark onto the bottom-right of every generated post infographic, on a small white rounded plate so it stays legible on any background (addRoxLogo via sharp; best-effort, falls back to unbranded). - Prompt: reserve a clean empty bottom-right area and forbid model-drawn logos/footer ribbons, so the overlaid logo never collides with content. - Drop the obsolete pixel-art 'nearest' thumbnail kernel. Verified across multiple generated samples: small logo, no overlap. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ew (#9) Collapsed comment previews showed raw markdown (e.g. "", "## …", "**…**"). Now the preview shows a small image thumbnail when the comment embeds an image, plus a markdown-stripped one-line snippet (image → alt text, links → text, symbols removed). Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restyles the BMC image prompt from the old Beach Science teal/sand palette to the modern ROX look: white canvas, cobalt (#0040FF) block header bars with white labels, charcoal content, coral (#FF3860) accent on Revenue Streams + the title rule, cobalt outer frame, bold geometric sans-serif, flat (no shadows). Keeps the standard Osterwalder 9-block grid. Matches the restyled post infographics. Co-authored-by: JesusJonesDE <6793481+JesusJonesDE@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
When the app runs behind a reverse proxy (e.g. AWS ALB),
request.urlin the OAuth callback handler resolves to the internal container address (http://localhost:3000) rather than the public domain. This causes users to be redirected tolocalhost:3000after completing Google OAuth sign-in.The fix uses
NEXT_PUBLIC_SITE_URL(if set) or theX-Forwarded-Hostheader as the redirect origin, falling back to theHostheader.Changes
src/app/auth/callback/route.ts: Construct redirect URL fromNEXT_PUBLIC_SITE_URLor forwarded headers instead ofrequest.urlTesting
NEXT_PUBLIC_SITE_URLis typically not set in dev, so it falls back toHostheader which islocalhost:3000as expected)