Skip to content

Comments

fix: block entering a room for not active accounts#256

Merged
iparaskev merged 3 commits intomainfrom
block_calls_rooms
Feb 11, 2026
Merged

fix: block entering a room for not active accounts#256
iparaskev merged 3 commits intomainfrom
block_calls_rooms

Conversation

@iparaskev
Copy link
Contributor

@iparaskev iparaskev commented Feb 9, 2026

Summary by CodeRabbit

  • New Features

    • Enforced subscription/active-trial checks before granting room/session access.
    • Added specific "trial expired" messaging in join flows and deep-link handling to clarify access failures.
  • Chores

    • Updated development tooling to a newer pre-commit lint version.

@iparaskev iparaskev requested a review from konsalex as a code owner February 9, 2026 22:54
@netlify
Copy link

netlify bot commented Feb 9, 2026

Deploy Preview for hoppdocs ready!

Name Link
🔨 Latest commit fdd7e41
🔍 Latest deploy log https://app.netlify.com/projects/hoppdocs/deploys/698c3b4c3a999100089c49f8
😎 Deploy Preview https://deploy-preview-256--hoppdocs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

Adds a centralized access check for subscriptions/trials used by backend token endpoints and websocket handlers, surfaces a specific "trial-ended" error to frontend UIs (including deep link handler and room UI), and bumps the golangci-lint pre-commit hook version.

Changes

Cohort / File(s) Summary
Configuration
./.pre-commit-config.yaml
Bumped golangci-lint pre-commit hook from v2.7.2 to v2.8.0.
Backend — Access helper
backend/internal/handlers/utils.go
Added checkUserHasAccess(db, user) to load subscription and return whether user is Pro or has an active (non-expired) trial.
Backend — Handlers gated by access
backend/internal/handlers/handlers.go, backend/internal/handlers/slackHandlers.go, backend/internal/handlers/websocketHandlers.go
Integrated access gating via checkUserHasAccess before generating tokens or accepting websocket actions. On denied access returns an error with "trial-ended" (handlers use 402 or 403 as applicable); subscription-fetch errors return 500. Removed inlined trial logic where replaced by helper.
Frontend — Surface trial-ended
tauri/src/lib/deepLinkUtils.ts, tauri/src/windows/main-window/tabs/Rooms.tsx
Handle backend trial-denied responses: deep link handler parses 402 to show trial-expired or generic payment message; Rooms UI shows a specific "Trial has expired…" toast when error.error === "trial-ended".

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Backend as Backend Handler
    participant DB as Database
    participant Telegram
    participant Frontend

    Client->>Backend: Request token / join
    activate Backend
    Backend->>DB: Load user + subscription (checkUserHasAccess)
    activate DB
    DB-->>Backend: subscription data / error
    deactivate DB

    alt Subscription fetch error
        Backend-->>Client: 500 {"error":"Failed to check subscription status"}
        Client->>Frontend: Show generic failure
    else User has access (Pro or active trial)
        Backend-->>Client: 200 {token...}
        Client->>Frontend: Proceed to session
    else Access denied (trial-ended)
        Backend->>Telegram: Alert unsubscribed attempt
        Backend-->>Client: 402/403 {"error":"trial-ended"}
        Client->>Frontend: Show "Trial has expired..." message
    end
    deactivate Backend
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through checks both near and far,
Trials, tokens, logged each star,
A toast for those whose trials have fled,
Alerts sent out for paths they tread,
Lint bumped up — a tidy little spar. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: block entering a room for not active accounts' accurately summarizes the main change across the pull request: implementing subscription/trial access checks to prevent inactive users from entering rooms.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch block_calls_rooms

No actionable comments were generated in the recent review. 🎉


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.

konsalex
konsalex previously approved these changes Feb 10, 2026
Comment on lines 561 to 576
// Check if user has access (paid or active trial)
userWithSub, err2 := models.GetUserWithSubscription(h.DB, user)
if err2 != nil {
c.Logger().Error("Error getting user subscription: ", err2)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to check subscription status")
}

hasAccess := userWithSub.IsPro
if !hasAccess && userWithSub.IsTrial && userWithSub.TrialEndsAt != nil {
hasAccess = userWithSub.TrialEndsAt.After(time.Now())
}

if !hasAccess {
return c.JSON(http.StatusForbidden, map[string]string{"error": "trial-ended"})
}

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: We have a flavour of this in 3 places, at some point it would be worth extracting this as a util

Copy link

@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: 2

🤖 Fix all issues with AI agents
In `@backend/internal/handlers/handlers.go`:
- Around line 1027-1037: The endpoint currently returns http.StatusForbidden
(403) when checkUserHasAccess returns false, which conflicts with the frontend
expecting 402 for trial-ended; change the response code in the failure branch
(where checkUserHasAccess(...) returns !hasAccess) from http.StatusForbidden to
http.StatusPaymentRequired and keep the same JSON body, and also update any
corresponding logic in slackHandlers.go that emits a 403 for trial-ended users
so both backend handlers consistently return 402 for expired trials (reference
symbols: checkUserHasAccess, notifications.SendTelegramNotification).

In `@backend/internal/handlers/utils.go`:
- Around line 204-208: checkUserHasAccess calls models.GetUserWithSubscription
which assumes user.TeamID is non-nil; add a nil guard for user.TeamID before
calling GetUserWithSubscription in checkUserHasAccess so we don't dereference a
nil *uint. Specifically, in checkUserHasAccess check if user.TeamID == nil and
handle that case (e.g., treat as no team / return appropriate access result or
skip subscription lookup) instead of calling GetUserWithSubscription, or
alternatively adapt the call site to pass a safe value; reference the functions
checkUserHasAccess and GetUserWithSubscription and the field user.TeamID when
making this change.

Comment on lines 1027 to 1037
// Check if caller has access (paid or active trial)
hasAccess, err := checkUserHasAccess(h.DB, user)
if err != nil {
c.Logger().Error("Error getting user subscription: ", err)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to check subscription status")
}

if !hasAccess {
_ = notifications.SendTelegramNotification(fmt.Sprintf("Unsubscribed user %s tried to join room %s", user.ID, room.Name), h.Config)
return c.JSON(http.StatusForbidden, map[string]string{"error": "trial-ended"})
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

HTTP 403 for trial-ended conflicts with the frontend's expected 402 status code.

deepLinkUtils.ts (Line 94) checks for response.status === 402 to handle trial expiration, but this endpoint returns http.StatusForbidden (403). The frontend's 403 handler (Line 101-102) would catch this instead and show "You don't have access to this session. It belongs to a different team." — a misleading message for expired trials.

Align on a single status code. 402 (Payment Required) is semantically appropriate for subscription/trial access gating. This would also require updating slackHandlers.go.

Proposed fix (if choosing 402)
 	if !hasAccess {
 		_ = notifications.SendTelegramNotification(fmt.Sprintf("Unsubscribed user %s tried to join room %s", user.ID, room.Name), h.Config)
-		return c.JSON(http.StatusForbidden, map[string]string{"error": "trial-ended"})
+		return c.JSON(http.StatusPaymentRequired, map[string]string{"error": "trial-ended"})
 	}
📝 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
// Check if caller has access (paid or active trial)
hasAccess, err := checkUserHasAccess(h.DB, user)
if err != nil {
c.Logger().Error("Error getting user subscription: ", err)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to check subscription status")
}
if !hasAccess {
_ = notifications.SendTelegramNotification(fmt.Sprintf("Unsubscribed user %s tried to join room %s", user.ID, room.Name), h.Config)
return c.JSON(http.StatusForbidden, map[string]string{"error": "trial-ended"})
}
// Check if caller has access (paid or active trial)
hasAccess, err := checkUserHasAccess(h.DB, user)
if err != nil {
c.Logger().Error("Error getting user subscription: ", err)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to check subscription status")
}
if !hasAccess {
_ = notifications.SendTelegramNotification(fmt.Sprintf("Unsubscribed user %s tried to join room %s", user.ID, room.Name), h.Config)
return c.JSON(http.StatusPaymentRequired, map[string]string{"error": "trial-ended"})
}
🤖 Prompt for AI Agents
In `@backend/internal/handlers/handlers.go` around lines 1027 - 1037, The endpoint
currently returns http.StatusForbidden (403) when checkUserHasAccess returns
false, which conflicts with the frontend expecting 402 for trial-ended; change
the response code in the failure branch (where checkUserHasAccess(...) returns
!hasAccess) from http.StatusForbidden to http.StatusPaymentRequired and keep the
same JSON body, and also update any corresponding logic in slackHandlers.go that
emits a 403 for trial-ended users so both backend handlers consistently return
402 for expired trials (reference symbols: checkUserHasAccess,
notifications.SendTelegramNotification).

@iparaskev iparaskev merged commit aafa151 into main Feb 11, 2026
16 checks passed
@iparaskev iparaskev deleted the block_calls_rooms branch February 11, 2026 08:28
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.

2 participants