Skip to content

Add all bridges#13

Open
kubo6472 wants to merge 25 commits intobeeper:mainfrom
tojemoc:save
Open

Add all bridges#13
kubo6472 wants to merge 25 commits intobeeper:mainfrom
tojemoc:save

Conversation

@kubo6472
Copy link
Copy Markdown
Contributor

@kubo6472 kubo6472 commented Nov 6, 2024

tested on my vercel, resolves #10

kubo6472 and others added 17 commits November 6, 2024 00:17
Bumps the npm_and_yarn group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [graphql](https://github.com/graphql/graphql-js) | `16.7.1` | `16.8.1` |
| [next](https://github.com/vercel/next.js) | `14.2.4` | `14.2.10` |
| [postcss](https://github.com/postcss/postcss) | `8.4.27` | `8.4.31` |
| [braces](https://github.com/micromatch/braces) | `3.0.2` | `3.0.3` |
| [micromatch](https://github.com/micromatch/micromatch) | `4.0.5` | `4.0.8` |



Updates `graphql` from 16.7.1 to 16.8.1
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](graphql/graphql-js@v16.7.1...v16.8.1)

Updates `next` from 14.2.4 to 14.2.10
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](vercel/next.js@v14.2.4...v14.2.10)

Updates `postcss` from 8.4.27 to 8.4.31
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.4.27...8.4.31)

Updates `braces` from 3.0.2 to 3.0.3
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](micromatch/braces@3.0.2...3.0.3)

Updates `micromatch` from 4.0.5 to 4.0.8
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](micromatch/micromatch@4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: graphql
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: braces
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: micromatch
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
…arn-40d9cccc4f

Bump the npm_and_yarn group across 1 directory with 5 updates
Bumps the npm_and_yarn group with 1 update in the / directory: [nanoid](https://github.com/ai/nanoid).


Updates `nanoid` from 3.3.6 to 3.3.8
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](ai/nanoid@3.3.6...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
…arn-af93afb32e

Bump nanoid from 3.3.6 to 3.3.8 in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 2 updates in the / directory: [next](https://github.com/vercel/next.js) and [cross-spawn](https://github.com/moxystudio/node-cross-spawn).


Updates `next` from 14.2.10 to 14.2.21
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](vercel/next.js@v14.2.10...v14.2.21)

Updates `cross-spawn` from 7.0.3 to 7.0.6
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](moxystudio/node-cross-spawn@v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: cross-spawn
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
…arn-b42a7f98e3

Bump the npm_and_yarn group across 1 directory with 2 updates
Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js).


Updates `next` from 14.2.21 to 14.2.30
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](vercel/next.js@v14.2.21...v14.2.30)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 14.2.30
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
…arn-3bd7c2c787

Bump next from 14.2.21 to 14.2.30 in the npm_and_yarn group across 1 directory
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 6, 2025

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added redeploy functionality for bridge instances
    • Added support for instagramgo and facebookgo bridges; removed twitter support
    • Copy button for bridge information
    • View on Fly link for bridge deployments
    • Session logout capability
  • Improvements

    • Updated Fly.io login guidance for org deploy tokens
  • Chores

    • Updated project dependencies (Next.js, ESLint, GraphQL, PostCSS)
    • Added Dependabot configuration

Walkthrough

Adds Dependabot config; implements a redeploy flow in the deploy API and UI (creates/attaches volumes, updates/creates Fly machines, polls for bridge readiness); adds Beeper logout API; updates BridgeInstance and Console for redeploy UI/UX; adjusts bridge mappings, login copy/inputs, repo link, package pins, and TS config; minor formatting tweaks.

Changes

Cohort / File(s) Summary
Dependabot Setup
​.github/dependabot.yml
New Dependabot config enabling weekly yarn updates at repo root.
Deploy API & Redeploy
app/api/deploy/route.ts
Adds redeploy branch: fetches Fly machines, updates machine image to ghcr.io/beeper/bridge-manager, creates and binds a volume, creates machines with mounts/services, expands secrets/release parsing, polls for bridge readiness, and returns success/error payloads.
Beeper Logout API
app/api/beeper/logout/route.ts
New POST endpoint forwarding beeperToken to Beeper’s Matrix logout endpoint and returning upstream status/errors.
BridgeInstance UI
app/components/BridgeInstance.tsx
Adds onRedeploy prop and redeploying state; implements redeployBridge POST to /api/deploy; adds "View on Fly", "Copy", Redeploy/Delete UI, and error messaging.
Console → Bridge wiring
app/components/Console.tsx
Adds bridgeRedeployed state, passes onRedeploy to BridgeInstance, adds Redeploy column header, and makes signOut async with upstream logout call.
Bridge Mapping
app/components/BridgeDeploy.tsx
Replaces instagram with instagramgo, adds facebookgo and signal, removes twitter.
Auth UIs
app/components/BeeperLogin.tsx, app/components/FlyLogin.tsx
BeeperLogin: strip whitespace from code input, switch to text input with numeric hints. FlyLogin: request an org deploy token, update link/label and button copy.
Welcome Link
app/components/Welcome.tsx
Updates repository link to https://github.com/kubo6472/beeper-selfhost/.
Package Metadata & Dependencies
package.json
Adds packageManager: "yarn@1.22.19"; bumps eslint9.26.0, graphql^16.8.1, next^16.2.3, postcss8.5.10.
TypeScript Config & Formatting
tsconfig.json
Changes JSX to react-jsx, expands/reformats lib/include/exclude arrays and path alias formatting (formatting-focused).

Sequence Diagram

sequenceDiagram
    participant UI as BridgeInstance UI
    participant API as /api/deploy Route
    participant Fly as Fly Machines API
    participant Beeper as Beeper App API

    UI->>API: POST /api/deploy { redeploy: true, appName, flyToken }
    API->>Fly: GET /v1/apps/{appName}/machines
    Fly-->>API: machines list
    API->>Fly: POST /v1/apps/{appName}/machines/{machineId}/updates (image: ghcr.io/beeper/bridge-manager)
    Fly-->>API: update response
    API->>Fly: POST /v1/apps/{appName}/volumes (create volume) -> returns volumeId
    Fly-->>API: volume creation response
    API->>Fly: POST /v1/apps/{appName}/machines (create machine with mounts/services)
    Fly-->>API: machine creation response
    loop Poll for bridge registration
        API->>Beeper: GET /api/apps/{appName}/bridges
        Beeper-->>API: bridge status
    end
    API-->>UI: 200 success or error (with details)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Multiple changes appear out of scope: Dependabot config, Next.js/package upgrades, login UI changes, Fly token type changes, logout endpoint, and deploy/redeploy infrastructure beyond bridge additions. Focus on bridge-related changes only [#10]. Move unrelated updates (dependency upgrades, login UI, logout endpoint, redeploy feature) to separate PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Add all bridges' is vague and overly broad; it doesn't clearly convey which specific bridges were added or the scope of changes. Use a more specific title that mentions the new bridges added (e.g., 'Add instagramgo, facebookgo, and signal bridges').
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description references issue #10 and indicates testing was performed, which relates to the changeset's bridge additions and deployments.
Linked Issues check ✅ Passed The PR successfully implements the linked issue #10 requirements: signal bridge is added in BridgeDeploy.tsx and instagramgo/facebookgo replace instagram/twitter.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Bumps the npm_and_yarn group with 3 updates in the / directory: [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime), [brace-expansion](https://github.com/juliangruber/brace-expansion) and [js-yaml](https://github.com/nodeca/js-yaml).


Updates `@babel/runtime` from 7.22.10 to 7.28.4
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.28.4/packages/babel-runtime)

Updates `brace-expansion` from 1.1.11 to 1.1.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@1.1.11...v1.1.12)

Updates `js-yaml` from 4.1.0 to 4.1.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-version: 7.28.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between db0eee9 and d493906.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • .github/dependabot.yml (1 hunks)
  • app/api/deploy/route.ts (4 hunks)
  • app/components/BeeperLogin.tsx (3 hunks)
  • app/components/BridgeDeploy.tsx (2 hunks)
  • app/components/BridgeInstance.tsx (2 hunks)
  • app/components/FlyLogin.tsx (1 hunks)
  • app/components/Welcome.tsx (1 hunks)
  • package.json (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/api/deploy/route.ts (1)
app/api/bridges/route.ts (1)
  • POST (4-63)
🔇 Additional comments (9)
app/components/FlyLogin.tsx (1)

15-19: LGTM! Clear documentation improvement.

The update to reference "org deploy token" with a direct link to the tokens page improves user guidance and aligns with Fly.io's authentication best practices.

.github/dependabot.yml (1)

1-11: LGTM! Standard Dependabot configuration.

The Dependabot setup is correctly configured for yarn with weekly updates, which will help keep dependencies current and secure.

package.json (2)

5-5: Good addition for dependency management.

The packageManager field helps ensure consistent package manager versions across environments when using Corepack.


19-22: Dependency versions are secure and verified.

All three versions are legitimate, secure releases:

  • graphql@16.8.1: Fixes CVE-2023-26144 (DoS vulnerability)
  • next@14.2.32: Fixes CVE-2025-57822 (SSRF in Middleware) and CVE-2025-57752 (image optimization cache issue from 14.2.31)
  • postcss@8.4.31: Fixes CVE-2023-44270 (parsing/injection vulnerability)

The Next.js update from 14.2.4 to 14.2.32 is a patch-level change within the same minor version (28 patch updates), not a significant version jump. No breaking changes are expected from these security-focused patch updates.

app/components/BeeperLogin.tsx (2)

36-37: Good UX improvement for code input handling.

Stripping whitespace allows users to paste codes formatted with spaces (like "123 456") and have them work correctly.


85-91: Better input type for numeric codes.

Changing from type="number" to type="text" with inputMode="numeric" is the correct approach for verification codes, as it prevents invalid characters (e, +, -, .) while still showing the numeric keyboard on mobile devices.

app/components/BridgeInstance.tsx (1)

57-62: Nice UX enhancements.

The Copy button for the bot handle and the View on Fly link provide helpful shortcuts for users to interact with their deployed bridges.

app/api/deploy/route.ts (1)

51-203: LGTM! Existing deploy logic preserved.

The formatting changes don't affect the deployment logic, and the flow remains intact with proper error handling and deployment verification.

app/components/BridgeDeploy.tsx (1)

47-49: Verify new bridge names are supported by the backend.

The bridge mapping now includes instagramgo, facebookgo, and signal (replacing the old instagram entry). Ensure these bridge identifiers match what the ghcr.io/beeper/bridge-manager container expects and are properly supported. Verify the changes are used consistently throughout the codebase and that no stale references to the old instagram bridge remain.

Comment thread app/api/deploy/route.ts
Comment on lines +7 to +49
// If redeploy flag is passed, update existing machine to latest image
if (redeploy) {
const res_list = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${flyToken}`,
'Content-Type': 'application/json',
}
})

if (res_list.status !== 200) {
const list_data = await res_list.json()
return NextResponse.json({ error: JSON.stringify(list_data) }, { status: 500 })
}

const machines = await res_list.json()
if (!machines || machines.length === 0) {
return NextResponse.json({ error: `No machines found for app ${appName}` }, { status: 404 })
}

const machine = machines[0]

const update_res = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${machine.id}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${flyToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
config: {
...machine.config,
image: "ghcr.io/beeper/bridge-manager"
}
})
})

if (update_res.status !== 200) {
const update_data = await update_res.json()
return NextResponse.json({ error: JSON.stringify(update_data) }, { status: 500 })
}

return NextResponse.json({ success: true })
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Redeploy logic needs improvements for robustness.

The redeploy implementation has several concerns:

  1. Line 27: Always uses the first machine without validation - if multiple machines exist, is this intentional?

  2. Line 38: The hardcoded image "ghcr.io/beeper/bridge-manager" has no version tag:

    • Docker may use cached images instead of pulling the latest
    • Consider using a specific tag or :latest explicitly
    • No guarantee the image will actually update
  3. No deployment verification: Unlike the main deploy flow (lines 182-200), the redeploy returns immediately without confirming the machine restarted or is running the new image

  4. Line 37: Spreading ...machine.config could be risky if the config schema has changed between bridge-manager versions

Consider these improvements:

 // If redeploy flag is passed, update existing machine to latest image
 if (redeploy) {
     const res_list = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines`, {
         method: 'GET',
         headers: {
             'Authorization': `Bearer ${flyToken}`,
             'Content-Type': 'application/json',
         }
     })

     if (res_list.status !== 200) {
         const list_data = await res_list.json()
         return NextResponse.json({ error: JSON.stringify(list_data) }, { status: 500 })
     }

     const machines = await res_list.json()
     if (!machines || machines.length === 0) {
         return NextResponse.json({ error: `No machines found for app ${appName}` }, { status: 404 })
     }

     const machine = machines[0]

     const update_res = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${machine.id}`, {
         method: 'POST',
         headers: {
             'Authorization': `Bearer ${flyToken}`,
             'Content-Type': 'application/json',
         },
         body: JSON.stringify({
             config: {
                 ...machine.config,
-                image: "ghcr.io/beeper/bridge-manager"
+                image: "ghcr.io/beeper/bridge-manager:latest"
             }
         })
     })

     if (update_res.status !== 200) {
         const update_data = await update_res.json()
         return NextResponse.json({ error: JSON.stringify(update_data) }, { status: 500 })
     }
+
+    // TODO: Consider adding verification that the machine restarted successfully

     return NextResponse.json({ success: true })
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If redeploy flag is passed, update existing machine to latest image
if (redeploy) {
const res_list = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${flyToken}`,
'Content-Type': 'application/json',
}
})
if (res_list.status !== 200) {
const list_data = await res_list.json()
return NextResponse.json({ error: JSON.stringify(list_data) }, { status: 500 })
}
const machines = await res_list.json()
if (!machines || machines.length === 0) {
return NextResponse.json({ error: `No machines found for app ${appName}` }, { status: 404 })
}
const machine = machines[0]
const update_res = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${machine.id}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${flyToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
config: {
...machine.config,
image: "ghcr.io/beeper/bridge-manager"
}
})
})
if (update_res.status !== 200) {
const update_data = await update_res.json()
return NextResponse.json({ error: JSON.stringify(update_data) }, { status: 500 })
}
return NextResponse.json({ success: true })
}
// If redeploy flag is passed, update existing machine to latest image
if (redeploy) {
const res_list = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${flyToken}`,
'Content-Type': 'application/json',
}
})
if (res_list.status !== 200) {
const list_data = await res_list.json()
return NextResponse.json({ error: JSON.stringify(list_data) }, { status: 500 })
}
const machines = await res_list.json()
if (!machines || machines.length === 0) {
return NextResponse.json({ error: `No machines found for app ${appName}` }, { status: 404 })
}
const machine = machines[0]
const update_res = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${machine.id}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${flyToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
config: {
...machine.config,
image: "ghcr.io/beeper/bridge-manager:latest"
}
})
})
if (update_res.status !== 200) {
const update_data = await update_res.json()
return NextResponse.json({ error: JSON.stringify(update_data) }, { status: 500 })
}
// TODO: Consider adding verification that the machine restarted successfully
return NextResponse.json({ success: true })
}
🤖 Prompt for AI Agents
In app/api/deploy/route.ts around lines 7 to 49, the redeploy branch is fragile:
it unconditionally picks the first machine, injects a hardcoded untagged image,
blindly spreads machine.config, and returns before verifying the update. Fix by
selecting the correct machine (validate if multiple exist and choose by
role/name or surface an error if ambiguous), require or append an explicit image
tag (or accept a tag param) instead of a bare registry string, build the new
config by copying only known-safe fields rather than blind ...machine.config
spreading, perform the machine update call and then poll the machine status
endpoint (with a short retry/backoff and a timeout) to verify the machine
restarted and is running the new image, and propagate detailed error responses
if selection, update, or verification fails.

Comment on lines +27 to +48
async function redeployBridge() {
setRedeploying(true)

const res = await fetch("/api/deploy", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
redeploy: true,
appName: name,
flyToken: flyToken
})
})

if (res.status !== 200) {
const error_data = await res.json()
setErrorMessage(error_data.error)
}

setRedeploying(false)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Improve redeploy error handling and user feedback.

The redeploy implementation has a few issues:

  1. The redeploying state is cleared even when an error occurs (line 47), which hides the error state from the user's perspective
  2. There's no success feedback after a successful redeploy
  3. The errorMessage from previous operations might still be displayed

Consider these improvements:

 async function redeployBridge() {
     setRedeploying(true)
+    setErrorMessage("") // Clear previous errors

     const res = await fetch("/api/deploy", {
         method: "POST",
         headers: {
             "Content-Type": "application/json"
         },
         body: JSON.stringify({
             redeploy: true,
             appName: name,
             flyToken: flyToken
         })
     })

     if (res.status !== 200) {
         const error_data = await res.json()
         setErrorMessage(error_data.error)
+        setRedeploying(false)
+        return
     }

+    // Success feedback
+    setErrorMessage("") // Clear any errors
+    // Consider adding success state/message
     setRedeploying(false)
 }
🤖 Prompt for AI Agents
In app/components/BridgeInstance.tsx around lines 27 to 48, improve redeploy
error handling by: clear any prior errorMessage before starting the request,
wrap the fetch in try/catch/finally, set a success message (e.g. "Redeploy
started") when res.status === 200, set errorMessage from the response in the
catch/failed branch, and move setRedeploying(false) into finally so redeploying
is only cleared after success or error handling is complete.

<div className="px-20">
<p className="mt-10">Use this web app to self-host bridges on your <a target="_blank" href={"https://fly.io/"} rel="noopener noreferrer">fly.io</a> account. Simply sign into Beeper and Fly, and this web app will run the bridge on your Fly account and install it in Beeper.</p>
<p className="mt-5">This site is open-source at <a target="_blank" href={"https://github.com/beeper/self-host-web"} rel="noopener noreferrer">https://github.com/beeper/self-host-web</a>. This webpage is being auto-deployed, however, you can run it from the GitHub if you prefer. Just follow the instructions in the README.</p>
<p className="mt-5">This site is open-source at <a target="_blank" href={"https://github.com/kubo6472/beeper-selfhost/"} rel="noopener noreferrer">https://github.com/kubo6472/beeper-selfhost</a>. This webpage is being auto-deployed, however, you can run it from the GitHub if you prefer. Just follow the instructions in the README.</p>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Confirm canonical GitHub URL (personal fork vs upstream).

Line 22 changes the “open‑source at” link to https://github.com/kubo6472/beeper-selfhost/, which appears to be a personal fork rather than the upstream project. For users of the main repo, this may be confusing and could send them to the wrong source of truth.

Unless the canonical project has actually moved to that URL, consider keeping (or switching back to) the upstream org repo instead:

-                <p className="mt-5">This site is open-source at <a target="_blank" href={"https://github.com/kubo6472/beeper-selfhost/"} rel="noopener noreferrer">https://github.com/kubo6472/beeper-selfhost</a>. This webpage is being auto-deployed, however, you can run it from the GitHub if you prefer. Just follow the instructions in the README.</p>
+                <p className="mt-5">This site is open-source at <a target="_blank" href={"https://github.com/beeper/self-host-web"} rel="noopener noreferrer">https://github.com/beeper/self-host-web</a>. This webpage is being auto-deployed, however, you can run it from the GitHub if you prefer. Just follow the instructions in the README.</p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p className="mt-5">This site is open-source at <a target="_blank" href={"https://github.com/kubo6472/beeper-selfhost/"} rel="noopener noreferrer">https://github.com/kubo6472/beeper-selfhost</a>. This webpage is being auto-deployed, however, you can run it from the GitHub if you prefer. Just follow the instructions in the README.</p>
<p className="mt-5">This site is open-source at <a target="_blank" href={"https://github.com/beeper/self-host-web"} rel="noopener noreferrer">https://github.com/beeper/self-host-web</a>. This webpage is being auto-deployed, however, you can run it from the GitHub if you prefer. Just follow the instructions in the README.</p>
🤖 Prompt for AI Agents
In app/components/Welcome.tsx around line 22, the "open-source at" link points
to a personal fork (https://github.com/kubo6472/beeper-selfhost/) rather than
the upstream canonical repository; verify which repository is intended (upstream
org vs this fork) and then update the href and visible URL to the canonical
upstream repo if the project hasn't moved, or add a short note clarifying that
this is a personal fork if that was intentional so users are directed to the
correct source of truth.

kubo6472 and others added 3 commits January 28, 2026 21:40
…arn-0497ab73d8

Bump the npm_and_yarn group across 1 directory with 3 updates
Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js).


Updates `next` from 14.2.32 to 16.1.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](vercel/next.js@v14.2.32...v16.1.5)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.1.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
…arn-a415b051ba

Bump next from 14.2.32 to 16.1.5 in the npm_and_yarn group across 1 directory
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 19-22: Update package.json to use eslint-config-next@16.x and
modify the "lint" script to run ESLint directly: change the dependency
"eslint-config-next" version from 13.4.13 to a 16.x range and replace the
existing "lint" script that invoked the removed "next lint" command with a
direct ESLint invocation (e.g., "lint": "eslint .") so the project uses the
correct Next.js 16 ESLint config and the script runs successfully.

Comment thread package.json Outdated
Comment on lines +19 to +22
"graphql": "^16.8.1",
"graphql-request": "^6.1.0",
"next": "^14.2.4",
"postcss": "8.4.27",
"next": "^16.1.5",
"postcss": "8.4.31",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Next.js 16 official release React peer dependencies requirements

💡 Result:

Next.js 16 officially released on October 21, 2025. (nextjs.org)

For Next.js 16.1.1, the React peer dependency requirements are: (unpkg.com)

  • react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
  • react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0

Citations:


🌐 Web query:

eslint-config-next versions compatibility Next.js 16

💡 Result:

For Next.js 16, you should use eslint-config-next 16.x (same major as next), because eslint-config-next pins @next/eslint-plugin-next to the exact same version (e.g., eslint-config-next@15.1.6 depends on @next/eslint-plugin-next@15.1.6). (app.unpkg.com)

Practical guidance (compatibility)

  • If your app is on next@16.*, install eslint-config-next@16.* (don’t keep eslint-config-next@15.*). (Next.js 16 is released; docs show latest 16.1.6.) (nextjs.org)
  • Next.js 16 removed next lint and the eslint option in next.config.js; run ESLint directly (or use the provided codemod). (nextjs.org)
  • Next.js 16 also shifts the Next ESLint plugin towards ESLint “flat config” by default. (nextjs.org)

What versions exist?

  • eslint-config-next has 16.0.1 stable releases (and canary 16.0.2 builds), so 16.x is available to match Next 16. (releasealert.dev)

Citations:


🏁 Script executed:

# Find and read the package.json to see the full dependency context
fd package.json -x head -40 {}

Repository: beeper/self-host-web

Length of output: 770


Upgrade eslint-config-next to 16.x and update the lint script.

Next.js 16 requires eslint-config-next@16.x to match the major version. The current eslint-config-next@13.4.13 is incompatible. Additionally, Next.js 16 removed the next lint command, so the lint script in package.json will fail—update it to run ESLint directly (e.g., "lint": "eslint ."). React 18.3.1 satisfies Next.js 16's requirements and does not need to be changed.

🤖 Prompt for AI Agents
In `@package.json` around lines 19 - 22, Update package.json to use
eslint-config-next@16.x and modify the "lint" script to run ESLint directly:
change the dependency "eslint-config-next" version from 13.4.13 to a 16.x range
and replace the existing "lint" script that invoked the removed "next lint"
command with a direct ESLint invocation (e.g., "lint": "eslint .") so the
project uses the correct Next.js 16 ESLint config and the script runs
successfully.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/Console.tsx (1)

10-27: Add bridgeRedeployed to the useEffect dependency array.
bridgeRedeployed is toggled by the redeploy handler but isn't in the dependency list, so the bridge list won't re-fetch after redeploy (unlike create and delete).

🛠️ Suggested fix
-    }, [bridgeDeleted, bridgeCreated]);
+    }, [bridgeDeleted, bridgeCreated, bridgeRedeployed]);
🤖 Fix all issues with AI agents
In `@app/components/FlyLogin.tsx`:
- Line 15: Update the hardcoded Fly.io token link in the FlyLogin component
paragraph to point to the current official org-scoped token docs; replace the
href value in the <p className="mt-4"> (the anchor inside that paragraph) from
"https://fly.io/tokens" to "https://fly.io/docs/flyctl/tokens-create-org/" so
users are directed to the correct org deploy token documentation.
🧹 Nitpick comments (3)
app/components/FlyLogin.tsx (1)

19-21: Associate the label with the token input for accessibility.

A <label htmlFor> improves screen‑reader support and click‑to‑focus behavior.

♿ Suggested tweak
-                <p>Your fly.io org deploy token:</p>
-                <form onSubmit={handleSubmit}>
-                    <input className="p-2 border-2 rounded-md w-full" type={"password"} />
+                <form onSubmit={handleSubmit}>
+                    <label htmlFor="flyToken" className="block">Your Fly.io org deploy token:</label>
+                    <input id="flyToken" className="p-2 border-2 rounded-md w-full" type="password" />
app/components/BridgeInstance.tsx (1)

74-79: Hide/disable Redeploy when onFly is false.
If the app isn’t on Fly, redeploy will always fail—consider gating the button.

♻️ Suggested tweak
-                {!redeploying ? (
-                    <button className="p-2 rounded-md m-4 bg-blue-600 border-0 text-white hover:bg-blue-500" onClick={redeployBridge}>Redeploy</button>
-                ) : (
-                    <button className="p-2 rounded-md m-4 bg-blue-300 border-0 text-white" disabled={true}>Redeploying...</button>
-                )}
+                {onFly && (!redeploying ? (
+                    <button className="p-2 rounded-md m-4 bg-blue-600 border-0 text-white hover:bg-blue-500" onClick={redeployBridge}>Redeploy</button>
+                ) : (
+                    <button className="p-2 rounded-md m-4 bg-blue-300 border-0 text-white" disabled={true}>Redeploying...</button>
+                ))}
app/api/deploy/route.ts (1)

136-206: Consider cleanup on partial failures to avoid orphaned resources.
If volume creation succeeds but machine creation (or later steps) fails, volumes/apps can be left behind. A compensating delete/rollback path would improve reliability and cost control.

<p className="mt-4">{"We'll"} use Fly, a cloud hosting provider, to deploy your bridges. You can run up to 3 bridges for free, then {"they'll"} charge you $2/month for each additional bridge.</p>
<p className="mt-4">Create a Fly account at <a target="_blank" href={"https://fly.io/app/sign-up"} rel="noopener noreferrer">https://fly.io/app/sign-up</a>.</p>
<p className="mt-4">Next, generate an access token: <a target="_blank" href={"https://fly.io/user/personal_access_tokens"} rel="noopener noreferrer">https://fly.io/user/personal_access_tokens</a>. This allows the self-host utility to deploy bridges on your Fly account. Your token is passed directly from this web app to Fly, and all of the code involving your Fly token is open-source on GitHub.</p>
<p className="mt-4">Next, generate an org deploy token: <a target="_blank" href={"https://fly.io/tokens"} rel="noopener noreferrer">https://fly.io/tokens</a>. This allows the self-host utility to deploy bridges on your Fly account. Your token is passed directly from this web app to Fly, and all of the code involving your Fly token is open-source on GitHub.</p>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Fly.io org deploy token documentation URL tokens

💡 Result:

Fly.io docs for org deploy tokens (and token docs in general):

  • Org-scoped deploy token (fly tokens create org): https://fly.io/docs/flyctl/tokens-create-org/ [1]
  • Access tokens overview (app-scoped vs org-scoped, best practices): https://fly.io/docs/security/tokens/ [2]
  • (Related) App-scoped deploy token (fly tokens create deploy): https://fly.io/docs/flyctl/tokens-create-deploy/ [3]

🌐 Web query:

site:fly.io/tokens page

💡 Result:

Fly.io “tokens” docs/pages (from site:fly.io/tokens page):

Sources: [1] [2] [3] [4] [5] [6] [8]


Update Fly.io org deploy token URL to the current official documentation.

The link https://fly.io/tokens does not match Fly.io's official documentation structure. Update it to https://fly.io/docs/flyctl/tokens-create-org/ for org-scoped deploy token guidance, or https://fly.io/docs/security/tokens/ for the broader tokens overview.

🤖 Prompt for AI Agents
In `@app/components/FlyLogin.tsx` at line 15, Update the hardcoded Fly.io token
link in the FlyLogin component paragraph to point to the current official
org-scoped token docs; replace the href value in the <p className="mt-4"> (the
anchor inside that paragraph) from "https://fly.io/tokens" to
"https://fly.io/docs/flyctl/tokens-create-org/" so users are directed to the
correct org deploy token documentation.

dependabot Bot and others added 3 commits April 27, 2026 11:37
Bumps the npm_and_yarn group with 1 update in the / directory: [eslint](https://github.com/eslint/eslint).


Updates `eslint` from 8.46.0 to 9.26.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/v9.26.0/CHANGELOG.md)
- [Commits](eslint/eslint@v8.46.0...v9.26.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.26.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Send Beeper API logout request on sign out
Bumps the npm_and_yarn group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [next](https://github.com/vercel/next.js) | `16.1.5` | `16.2.3` |
| [postcss](https://github.com/postcss/postcss) | `8.4.31` | `8.5.10` |
| [@hono/node-server](https://github.com/honojs/node-server) | `1.19.9` | `1.19.14` |
| [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | `1.25.3` | `1.29.0` |
| [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.12` | `1.1.14` |
| [flatted](https://github.com/WebReflection/flatted) | `3.3.3` | `3.4.2` |
| [minimatch](https://github.com/isaacs/minimatch) | `3.1.2` | `3.1.5` |
| [path-to-regexp](https://github.com/pillarjs/path-to-regexp) | `8.3.0` | `8.4.2` |
| [picomatch](https://github.com/micromatch/picomatch) | `2.3.1` | `2.3.2` |
| [qs](https://github.com/ljharb/qs) | `6.14.1` | `6.15.1` |
| [yaml](https://github.com/eemeli/yaml) | `2.3.1` | `2.8.3` |



Updates `next` from 16.1.5 to 16.2.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](vercel/next.js@v16.1.5...v16.2.3)

Updates `postcss` from 8.4.31 to 8.5.10
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.4.31...8.5.10)

Updates `@hono/node-server` from 1.19.9 to 1.19.14
- [Release notes](https://github.com/honojs/node-server/releases)
- [Commits](honojs/node-server@v1.19.9...v1.19.14)

Updates `@modelcontextprotocol/sdk` from 1.25.3 to 1.29.0
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](modelcontextprotocol/typescript-sdk@v1.25.3...v1.29.0)

Updates `brace-expansion` from 1.1.12 to 1.1.14
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v1.1.12...v1.1.14)

Updates `flatted` from 3.3.3 to 3.4.2
- [Commits](WebReflection/flatted@v3.3.3...v3.4.2)

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v3.1.2...v3.1.5)

Updates `path-to-regexp` from 8.3.0 to 8.4.2
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](pillarjs/path-to-regexp@v8.3.0...v8.4.2)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](micromatch/picomatch@2.3.1...2.3.2)

Updates `qs` from 6.14.1 to 6.15.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](ljharb/qs@v6.14.1...v6.15.1)

Updates `yaml` from 2.3.1 to 2.8.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](eemeli/yaml@v2.3.1...v2.8.3)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.2.3
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.10
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: "@hono/node-server"
  dependency-version: 1.19.14
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.29.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.14
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: path-to-regexp
  dependency-version: 8.4.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: qs
  dependency-version: 6.15.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 2.8.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/api/beeper/logout/route.ts (1)

3-16: Wrap network/parse calls in try/catch to avoid leaking 500s.

Two unhandled failure modes here:

  • Line 4: req.json() throws on an empty/invalid body, surfacing as a generic 500 instead of a clean 400.
  • Lines 10–16: A network error (DNS failure, connection reset, etc.) on the outbound fetch propagates as an unhandled exception. Consider also adding a request timeout (AbortSignal.timeout(...)) so the route doesn't hang on a stalled upstream.
♻️ Suggested structure
 export async function POST(req: Request) {
-    const { beeperToken } = await req.json();
+    let beeperToken: string | undefined;
+    try {
+        ({ beeperToken } = await req.json());
+    } catch {
+        return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
+    }

     if (!beeperToken) {
         return NextResponse.json({ error: "Missing beeperToken" }, { status: 400 });
     }

-    const response = await fetch("https://matrix.beeper.com/_matrix/client/v3/logout", {
-        method: "POST",
-        headers: {
-            Authorization: `Bearer ${beeperToken}`,
-            "Content-Type": "application/json",
-        },
-    });
+    let response: Response;
+    try {
+        response = await fetch("https://matrix.beeper.com/_matrix/client/v3/logout", {
+            method: "POST",
+            headers: {
+                Authorization: `Bearer ${beeperToken}`,
+                "Content-Type": "application/json",
+            },
+            signal: AbortSignal.timeout(10_000),
+        });
+    } catch (e) {
+        return NextResponse.json(
+            { error: "Network error contacting Beeper API", details: String(e) },
+            { status: 502 },
+        );
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/beeper/logout/route.ts` around lines 3 - 16, Wrap the POST handler's
parsing and network call in try/catch: catch errors from req.json() (in POST)
and return a 400 JSON response when the body is invalid/missing, and catch
errors from the outbound fetch to return a controlled error response (e.g.,
502/504) instead of letting exceptions bubble to a 500; also use an
AbortSignal.timeout (or AbortController with timeout) when calling fetch to
avoid hanging on stalled upstreams. Locate and update the POST function, the
req.json() call, and the fetch to add the timeout/abort and error handling, and
ensure all error branches use NextResponse.json with appropriate status codes
and helpful messages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/beeper/logout/route.ts`:
- Around line 18-30: The failure handling for the Beeper logout uses
response.json() then response.text(), which consumes the body twice and can
throw "Body is unusable"; instead read the body once as text (use await
response.text()), then attempt JSON.parse on that text inside a try/catch to
produce structured details, falling back to the raw text if parse fails, and
finally return NextResponse.json({ error: "Failed to logout from Beeper API",
details }, { status: response.status }); update the error branch in route.ts
(the block using the response variable and NextResponse.json) to implement this
single-read approach.

---

Nitpick comments:
In `@app/api/beeper/logout/route.ts`:
- Around line 3-16: Wrap the POST handler's parsing and network call in
try/catch: catch errors from req.json() (in POST) and return a 400 JSON response
when the body is invalid/missing, and catch errors from the outbound fetch to
return a controlled error response (e.g., 502/504) instead of letting exceptions
bubble to a 500; also use an AbortSignal.timeout (or AbortController with
timeout) when calling fetch to avoid hanging on stalled upstreams. Locate and
update the POST function, the req.json() call, and the fetch to add the
timeout/abort and error handling, and ensure all error branches use
NextResponse.json with appropriate status codes and helpful messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6519df02-b062-4f28-9988-85b0a4e37b77

📥 Commits

Reviewing files that changed from the base of the PR and between bf3dcc2 and a521bbd.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • app/api/beeper/logout/route.ts
  • app/components/Console.tsx
  • package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/components/Console.tsx
  • package.json

Comment on lines +18 to +30
if (!response.ok) {
let details = "";
try {
details = JSON.stringify(await response.json());
} catch {
details = await response.text();
}

return NextResponse.json(
{ error: "Failed to logout from Beeper API", details },
{ status: response.status }
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fallback path is broken: response body already consumed.

response.json() reads and locks the body stream. If parsing fails (e.g. upstream returned HTML or plain text), the catch block's await response.text() will throw TypeError: Body is unusable / already read, which propagates as an unhandled 500 and discards both the upstream status and any diagnostic info you intended to forward.

Read the body once as text, then try to parse it as JSON:

🐛 Proposed fix
     if (!response.ok) {
-        let details = "";
-        try {
-            details = JSON.stringify(await response.json());
-        } catch {
-            details = await response.text();
-        }
+        const raw = await response.text();
+        let details: unknown = raw;
+        try {
+            details = JSON.parse(raw);
+        } catch {
+            // keep raw text
+        }

         return NextResponse.json(
             { error: "Failed to logout from Beeper API", details },
             { status: response.status }
         );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!response.ok) {
let details = "";
try {
details = JSON.stringify(await response.json());
} catch {
details = await response.text();
}
return NextResponse.json(
{ error: "Failed to logout from Beeper API", details },
{ status: response.status }
);
}
if (!response.ok) {
const raw = await response.text();
let details: unknown = raw;
try {
details = JSON.parse(raw);
} catch {
// keep raw text
}
return NextResponse.json(
{ error: "Failed to logout from Beeper API", details },
{ status: response.status }
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/beeper/logout/route.ts` around lines 18 - 30, The failure handling
for the Beeper logout uses response.json() then response.text(), which consumes
the body twice and can throw "Body is unusable"; instead read the body once as
text (use await response.text()), then attempt JSON.parse on that text inside a
try/catch to produce structured details, falling back to the raw text if parse
fails, and finally return NextResponse.json({ error: "Failed to logout from
Beeper API", details }, { status: response.status }); update the error branch in
route.ts (the block using the response variable and NextResponse.json) to
implement this single-read approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add new meta bridge(s) and the new signal bridge

1 participant