Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@e-esakman
Copy link
Contributor

@e-esakman e-esakman commented Dec 15, 2025

Description

This PR updates the messaging system to allow users to send chat requests to others who have not yet unlocked their chat feature, improving the UX for making interactions more dynamic.

Issue

Previously, when searching for a user who hadn’t unlocked their chats, the “Message” button was unavailable. This led to a poor user experience, as there was no way to initiate communication.

Changes

"Request Chat” button has been added which sends an email notification to the recipient, allowing them to unlock their chats and respond if they wish.

Key changes

  • models.py — add ChatRequest model and slug field for User.
  • core.py — chat request creation logic.
  • user.py — update start_thread logic to handle new request flow.
  • search.html — change message box UI and button logic for messaging actions.

Testing

  1. Run the local server - poetry run daphne blt.asgi:application
  2. Email service - poetry run python -m smtpd -n -c DebuggingServer localhost:1025

Additionally , it fixes #3860 ensuring email initiation works both for locked and unlocked chat cases when a new user initiates a message.

Untitled.design.1.mp4

Summary by CodeRabbit

  • New Features

    • "Request Chat" button and "Request Sent" state for pending chat requests.
    • User profiles gain timezone support and unique profile slugs.
    • App category list expanded with Messaging, Gaming, Streaming, and Other.
  • Behavior Changes

    • Prevents rapid or duplicate chat requests with rate limiting and duplicate checks.
    • Locked recipients trigger a request flow and notification email instead of immediate chat.
    • Unified client-side flow for starting or requesting chats with improved UI feedback.
  • Tests

    • Added tests for chat requests, duplicate blocking, and rate limiting.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

👋 Hi @e-esakman!

This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:

  • The PR author
  • DonnieBLT
  • coderabbitai
  • copilot

Once a valid peer review is submitted, this check will pass automatically. Thank you!

@github-actions github-actions bot added files-changed: 5 PR changes 5 files needs-peer-review PR needs peer review migrations PR contains database migration files labels Dec 15, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds ChatRequest model and migration, adds slug and timezone to UserProfile with save logic, introduces Redis-backed rate limiting and a send_chat_request endpoint, updates start-thread flow for locked recipients, adjusts search UI/JS to request chats, adds route and tests.

Changes

Cohort / File(s) Summary
Database Migration
website/migrations/0264_add_slug_timezone_and_chatrequest.py
Adds slug and timezone fields to UserProfile; creates ChatRequest model with sender/receiver FKs, created_at, is_unlocked, email_sent, and unique_together on (sender, receiver).
Models
website/models.py
Adds UserProfile.slug and UserProfile.timezone and a UserProfile.save override to populate a unique slug; introduces ChatRequest model; expands BannedApp.APP_TYPES; removes Tag auto-slug save logic and Tag __str__.
Search UI / JS
website/templates/search.html
Replaces single "Message" button with "Request Chat" / "Request Sent" states; consolidates client JS to POST to start-thread endpoint, handle locked/success responses, and manage button state with CSRF-aware fetch.
Core Views / Chat Request Logic
website/views/core.py
Adds can_send_chat_request(sender_id, receiver_id) with Redis-backed rate limiting and send_chat_request POST view that prevents self-requests/duplicates, creates ChatRequest, attempts email notification, logs outcomes, and returns JSON; search flow prefetches badges/chat-request state and annotates has_pending_request.
Thread Initialization / User Views
website/views/user.py
Makes start_thread POST-only; when recipient lacks public_key creates/gets ChatRequest, sends unlock email if not already sent and returns locked status; for unlocked recipients finds/creates thread, optionally emails recipient, and returns thread_id.
Routing
blt/urls.py
Adds URL pattern chat/request/<int:receiver_id>/ mapped to send_chat_request.
Tests
website/tests/test_core.py
Adds ChatRequestTests with setup for sender/receiver and tests for duplicate-request blocking and Redis-backed rate limiting (expects 429 on limit violations).

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Sender (browser)
    participant UI as Search UI (client JS)
    participant App as Django view (send_chat_request / start_thread)
    participant Redis as Redis (rate limiter)
    participant DB as Database (ChatRequest / Thread)
    participant Email as Email service

    Browser->>UI: Click "Request Chat" / "Message"
    UI->>App: POST /chat/request/{receiver_id} or /messaging/start-thread/{userId}/ (with CSRF)
    App->>Redis: check/increment rate limit for sender->receiver
    alt rate limit exceeded
        Redis-->>App: limit reached
        App-->>UI: 429 / {"error":"rate_limit"}
        UI->>Browser: show error / revert button
    else rate OK
        App->>DB: lookup existing ChatRequest
        alt existing record and locked
            DB-->>App: ChatRequest exists (locked/unlocked)
            App-->>UI: {"locked": true, "email_sent": ...}
        else create new ChatRequest
            App->>DB: create ChatRequest (sender, receiver)
            DB-->>App: ChatRequest created
            App->>Email: send unlock/notification email (if needed)
            Email-->>App: delivery status
            App->>DB: mark email_sent = true (if sent)
            App-->>UI: {"locked": true, "email_sent": true} or {"success": true, "thread_id": id}
            UI->>Browser: update button state or redirect to thread
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect Redis key format, TTL, atomic increment and fallback path for Redis failures.
  • Check ChatRequest get/create handling for unique_together races (transactions or get_or_create).
  • Verify email sending paths mark email_sent reliably and handle delivery/failure without inconsistent DB state.
  • Review UserProfile.slug uniqueness logic and collision handling when deriving from usernames.
  • Validate search prefetch/select_related to avoid N+1 and correct has_pending_request annotation.
  • Confirm JS CSRF handling, response parsing, and UI state transitions.

Suggested labels

quality: medium

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several changes appear tangential to the main objective: modifications to BannedApp.APP_TYPES and Tag model changes are unrelated to chat request functionality. Remove unrelated changes to BannedApp.APP_TYPES and Tag model modifications. Focus only on ChatRequest, UserProfile (slug/timezone), and email notification flow changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 accurately describes the main feature: enabling chat requests to locked users, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR implements the core requirement from issue #3860: sending email notifications to recipients when chat is initiated, supporting both locked and unlocked recipient states.

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.

@github-actions
Copy link
Contributor

📊 Monthly Leaderboard

Hi @e-esakman! Here's how you rank for December 2025:

Rank User PRs Reviews Comments Total
#6 @arnavkirti 4 12 27 142
#7 @e-esakman 4 3 15 82
#8 @swaparup36 3 6 10 74

Leaderboard based on contributions in December 2025. Keep up the great work! 🚀

@github-actions github-actions bot added the pre-commit: passed Pre-commit checks passed label Dec 15, 2025
@github-actions github-actions bot added the tests: failed Django tests failed label Dec 15, 2025
Copy link
Contributor

@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

🧹 Nitpick comments (3)
website/views/user.py (1)

1719-1794: start_thread flow looks correct; consider tightening a couple of edges

The locked/unlocked split, single-email behaviour via email_sent, and thread creation all look consistent with the new ChatRequest model and the front-end expectations.

Two non-blocking tweaks you may want to consider:

  • Add a cheap guard against self-chat to avoid weird UX like emailing a user to unlock their own chat with themselves:
@login_required
def start_thread(request, user_id):
    if request.method != "POST":
        return JsonResponse({"success": False, "error": "Invalid request"}, status=400)

    current_user = request.user
    other_user = get_object_or_404(User, id=user_id)
+
+    if other_user == current_user:
+        return JsonResponse({"success": False, "error": "Cannot start a thread with yourself"}, status=400)
  • If there’s any chance other_user.userprofile might be missing, you could defensively handle UserProfile.DoesNotExist instead of relying on the getattr shortcut; this is in line with other assumptions in this file though, so treat it as optional.
website/views/core.py (1)

49-77: User search + pending-request wiring is sound; small UX gaps you may want to close

The changes to search() correctly:

  • Load users with their UserProfile and scores.
  • Compute sent_requests via ChatRequest for the current user.
  • Attach has_pending_request and badges to each user object for the type='users' view, matching the template’s expectations.

Two minor UX details to consider (not blockers):

  1. type='all' search doesn’t reflect pending requests

    For the "all" search path, users is a plain queryset of User without the has_pending_request flag or badges attached. The template’s “Users” section still renders for that case and will therefore always show “Request Chat” instead of “Request Sent” even when a ChatRequest already exists.

    If you want consistent behaviour between "all" and "users" searches, you could reuse the same sent_requests/users_list construction for the "all" case (or refactor into a helper used by both paths).

  2. Distinguishing “request exists but no email was sent” (optional)

    has_pending_request is currently keyed purely on the presence of a ChatRequest. In the rare case where a request was created while receiver.email was empty, the UI will still show “Request Sent” later even though no email was ever delivered. If that distinction matters, you might drive the “Request Sent” label from email_sent (and potentially allow re-request if email_sent is still false).

Overall the logic is correct; these are UX-level refinements you can choose to defer.

Also applies to: 603-669

website/templates/search.html (1)

322-340: Unified Message / Request Chat UI matches backend; only nit is some dead class handling

The template + JS wiring lines up nicely with the new start_thread JSON API:

  • Correctly shows:
    • “Message” when user.userprofile.public_key exists.
    • Disabled “Request Sent” when user.has_pending_request is true.
    • “Request Chat” otherwise, using the same POST endpoint and relying on data.locked vs data.success to decide between “Request Sent” and redirect.
  • CSRF handling via getCookie("csrftoken") and POST is appropriate for this view.

One tiny clean-up you may want to make:

  • In the locked branch you call:

    this.classList.remove("bg-[#e74c3c]", "hover:bg-[#c0392b]");
    this.classList.add("bg-red-500", "cursor-not-allowed");

    but none of the current buttons use bg-[#e74c3c] or hover:bg-[#c0392b] anymore, so those removes are no-ops. You could either update the removals to match the current grey/indigo classes (if you want to normalise the visual state) or just drop the classList.remove call entirely to reduce noise.

Behaviour-wise everything is solid.

Also applies to: 785-848

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between cd7312d and 21ce883.

📒 Files selected for processing (5)
  • website/migrations/0264_add_slug_timezone_and_chatrequest.py (1 hunks)
  • website/models.py (4 hunks)
  • website/templates/search.html (2 hunks)
  • website/views/core.py (3 hunks)
  • website/views/user.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
website/views/user.py (2)
website/models.py (2)
  • ChatRequest (3089-3103)
  • Thread (3064-3069)
website/static/js/invite.js (1)
  • subject (75-75)
website/views/core.py (1)
website/models.py (2)
  • ChatRequest (3089-3103)
  • UserBadge (1682-1696)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (3)
website/models.py (3)

906-906: LGTM!

The slug field definition is appropriate. Using unique=True with null=True is valid in Django since NULL values are excluded from uniqueness checks.


935-935: LGTM!

The timezone field with max_length=50 and default "UTC" is appropriate for storing timezone identifiers.


3089-3103: LGTM!

The ChatRequest model is well-designed for the chat request feature:

  • Appropriate use of CASCADE on delete for user references
  • unique_together constraint correctly prevents duplicate requests between the same users
  • Clear status tracking with email_sent and is_unlocked fields

@github-project-automation github-project-automation bot moved this from Backlog to Ready in 📌 OWASP BLT Project Board Dec 15, 2025
@e-esakman e-esakman marked this pull request as draft December 15, 2025 13:02
@github-actions github-actions bot added the changes-requested PR has requested changes from a reviewer label Dec 15, 2025
@github-actions github-actions bot added tests: passed Django tests passed and removed tests: failed Django tests failed labels Dec 15, 2025
@e-esakman e-esakman marked this pull request as ready for review December 15, 2025 15:02
@github-actions github-actions bot removed the changes-requested PR has requested changes from a reviewer label Dec 15, 2025
Copy link
Contributor

@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

♻️ Duplicate comments (1)
website/models.py (1)

909-969: UserProfile.slug should be slugified and collision-safe (issue already raised previously)

UserProfile.slug is a SlugField(unique=True, …), but save() assigns self.user.username directly:

if not self.slug:
    self.slug = self.user.username

Django usernames can contain characters (@, ., +, etc.) that are invalid for SlugField validators and URL slug converters, and this also doesn’t handle collisions if you later derive slugs differently. This matches the concern in the earlier review comment that suggested using slugify() instead of raw username.

A safer implementation would mirror the slug logic used on Organization/Repo:

class UserProfile(models.Model):
@@
-    slug = models.SlugField(unique=True, blank=True, null=True)
+    slug = models.SlugField(unique=True, blank=True, null=True)
@@
-    def save(self, *args, **kwargs):
-        if not self.slug:
-            self.slug = self.user.username
-        super().save(*args, **kwargs)
+    def save(self, *args, **kwargs):
+        if not self.slug and self.user_id:
+            # Start from a slugified username
+            base_slug = slugify(self.user.username)
+            if not base_slug:
+                base_slug = f"user-{self.user_id}"
+
+            self.slug = base_slug
+            counter = 1
+            while UserProfile.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
+                self.slug = f"{base_slug}-{counter}"
+                counter += 1
+
+        super().save(*args, **kwargs)

This preserves existing custom slugs, ensures the value is a valid slug, and avoids collisions.

Check Django’s `SlugField` and `slugify` documentation to confirm valid characters for slugs and common patterns for generating unique slugs from usernames.
🧹 Nitpick comments (2)
website/models.py (2)

3092-3106: ChatRequest model is solid; optional CheckConstraint to prevent self-requests

The ChatRequest model structure (ForeignKeys to AUTH_USER_MODEL, unique_together = ("sender", "receiver"), and clear __str__) is well-designed.

If your domain requires preventing users from requesting a chat with themselves, you can add a DB-level check constraint:

class ChatRequest(models.Model):
     created_at = models.DateTimeField(auto_now_add=True)

     class Meta:
         unique_together = ("sender", "receiver")
+        constraints = [
+            models.CheckConstraint(
+                check=~models.Q(sender=models.F("receiver")),
+                name="chatrequest_sender_not_receiver",
+            ),
+        ]

Django 5.2.x fully supports this constraint pattern with F() expressions. Alternatively, add validation in the view/service layer if preferred.


909-909: Consider consolidating timezone handling between UserProfile and ReminderSettings

Both models define identical timezone = models.CharField(max_length=50, default="UTC") fields. However, ReminderSettings has proper validation via its save() method (which uses pytz.timezone()) and provides get_timezone_choices() returning pytz.common_timezones. UserProfile.timezone lacks this validation and integration. To avoid divergence, either reuse ReminderSettings' timezone approach in UserProfile or consolidate to a single timezone management pattern across both models.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 21ce883 and f99b001.

📒 Files selected for processing (3)
  • website/migrations/0264_add_slug_timezone_and_chatrequest.py (1 hunks)
  • website/models.py (5 hunks)
  • website/views/user.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • website/views/user.py
  • website/migrations/0264_add_slug_timezone_and_chatrequest.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test

@github-actions github-actions bot added the changes-requested PR has requested changes from a reviewer label Dec 15, 2025
@github-actions github-actions bot added tests: passed Django tests passed and removed tests: failed Django tests failed labels Dec 16, 2025
@github-actions github-actions bot removed the changes-requested PR has requested changes from a reviewer label Dec 16, 2025
Copy link
Contributor

@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

♻️ Duplicate comments (2)
website/views/core.py (2)

646-656: Restore sorting and pagination to user search query.

The query is missing .order_by("-total_score")[0:20] that was present in the original implementation. Without this:

  • All matching UserProfile objects are fetched from the database (no limit)
  • Results are unsorted, making them less relevant
  • This causes performance degradation, especially with many users

This issue was previously flagged in past reviews (Reference ID: 7531570).

Apply this diff to restore sorting and limiting:

         elif stype == "users":
             users = (
                 UserProfile.objects.filter(user__username__icontains=query)
                 .select_related("user")
                 .prefetch_related(
                     Prefetch(
                         "user__userbadge_set",
                         queryset=UserBadge.objects.select_related("badge"),
                         to_attr="badges",
                     )
                 )
+                .order_by("-total_score")[0:20]
             )

902-928: Improve time-based fallback for rate limiting.

The Redis fallback on line 911 only checks if any ChatRequest exists between the two users, not whether the rate limit is actually exceeded within a time window. This means:

  • If Redis is unavailable and a ChatRequest exists, no new requests are ever allowed (regardless of time)
  • The fallback doesn't enforce the intended "3 per minute" limit

A previous review comment suggested implementing a time-based DB fallback (Reference ID comment about weak rate limit fallback).

Consider this time-based fallback:

     try:
         redis_conn = get_redis_connection("default")
     except Exception:
         logger.warning("Redis unavailable for chat request rate limiting")
-        return ChatRequest.objects.filter(sender_id=sender_id, receiver_id=recipient_id).count() < 1
+        one_minute_ago = timezone.now() - timedelta(minutes=1)
+        recent_sender_requests = ChatRequest.objects.filter(
+            sender_id=sender_id, created_at__gte=one_minute_ago
+        ).count()
+        recent_recipient_requests = ChatRequest.objects.filter(
+            receiver_id=recipient_id, created_at__gte=one_minute_ago
+        ).count()
+        return recent_sender_requests <= 3 and recent_recipient_requests <= 3
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between c3b62c9 and 62e6ae0.

📒 Files selected for processing (1)
  • website/views/core.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
website/views/core.py (1)
website/models.py (4)
  • Badge (1667-1681)
  • ChatRequest (3091-3105)
  • UserProfile (893-1118)
  • UserBadge (1684-1698)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run pre-commit
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (2)
website/views/core.py (2)

37-37: LGTM: Import additions support new chat request functionality.

The added imports (Prefetch, get_redis_connection, ChatRequest) are appropriately used for query optimization and the new chat request rate limiting feature.

Also applies to: 48-48, 54-54


604-604: LGTM: Query optimization added.

The select_related("userprofile") and distinct() additions properly optimize the user query to avoid N+1 queries while maintaining the result limit.

@github-actions github-actions bot added tests: failed Django tests failed and removed tests: passed Django tests passed labels Dec 16, 2025
@e-esakman
Copy link
Contributor Author

@coderabbitai The send_chat_request endpoint in core.py is intentionally limited to core domain logic only (validation, rate limiting, duplicate prevention, and ChatRequest creation). Email notifications and unlock-link generation are already handled in the existing user flow (website/views/user.py), which fixes notification behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the send_chat_request implementation in core.py
echo "=== website/views/core.py - send_chat_request function ==="
ast-grep --pattern $'def send_chat_request($$$) {
  $$$
}'

echo -e "\n=== website/views/core.py - can_send_chat_request function ==="
ast-grep --pattern $'def can_send_chat_request($$$) {
  $$$
}'

Length of output: 122


🏁 Script executed:

#!/bin/bash
# Read the start_thread implementation in user.py
echo "=== website/views/user.py - start_thread function ==="
ast-grep --pattern $'def start_thread($$$) {
  $$$
}'

Length of output: 53


🏁 Script executed:

#!/bin/bash
# Check where send_chat_request is called from
echo "=== Usage of send_chat_request endpoint ==="
rg -n "send_chat_request" --type=py -C 2

echo -e "\n=== URL routing for chat request ==="
rg -n "chat/request" blt/urls.py -A 2 -B 2

Length of output: 2395


🏁 Script executed:

#!/bin/bash
# Read the actual implementation using cat with line numbers
echo "=== website/views/core.py - send_chat_request and can_send_chat_request ==="
cat -n website/views/core.py | sed -n '902,960p'

Length of output: 2922


🏁 Script executed:

#!/bin/bash
# Read the start_thread implementation in user.py
echo "=== website/views/user.py - start_thread function ==="
rg -n "def start_thread" website/views/user.py -A 50

Length of output: 2300


🏁 Script executed:

#!/bin/bash
# Check what happens in the frontend when request-chat is clicked
echo "=== Frontend chat request handling ==="
rg -n "request-chat" website/templates/search.html -B 5 -A 10

Length of output: 2165


🏁 Script executed:

#!/bin/bash
# Read the complete JavaScript handling for chat requests
echo "=== Complete frontend JavaScript for chat handling ==="
rg -n "document.addEventListener.*DOMContentLoaded" website/templates/search.html -A 60 | head -80

Length of output: 2848


🏁 Script executed:

#!/bin/bash
# Check the complete start_thread function to see email handling
echo "=== Complete start_thread function ==="
cat -n website/views/user.py | sed -n '1720,1800p'

Length of output: 3639


@e-esakman Thank you for explaining the architectural decision. I can see the intent to separate core domain logic (validation, rate limiting, duplicate prevention) from the user-facing flow (email notifications, thread creation).

However, after reviewing the implementation, I noticed a potential issue:

The send_chat_request endpoint appears to be unused.

Looking at the frontend code in search.html (lines 786-797), both .start-chat and .request-chat buttons POST to the same endpoint:

const response = await fetch(`/messaging/start-thread/${userId}/`, {
  method: "POST",
  // ...
});

This means:

  1. All chat requests flow through start_thread in user.py - which handles locked users (lines 1728-1763) by creating ChatRequest and sending unlock emails
  2. The /chat/request/<receiver_id>/ endpoint defined in core.py is never called by the frontend
  3. Rate limiting in send_chat_request (lines 938-940) is effectively bypassed since that endpoint isn't in the execution path

Recommendation:

Either:

  • Option A: Wire the .request-chat button to call /chat/request/${userId}/ instead, then have start_thread focus only on unlocked users
  • Option B: Remove the unused send_chat_request endpoint and keep all logic consolidated in start_thread (current working behavior)
  • Option C: Have start_thread delegate to send_chat_request internally for locked users

The current implementation works correctly for the user experience, but the architectural separation you described isn't reflected in the actual code flow. Would you like to adjust the implementation to match the intended architecture?

@e-esakman e-esakman marked this pull request as draft December 16, 2025 07:57
@e-esakman e-esakman marked this pull request as ready for review December 16, 2025 08:24
@github-actions github-actions bot added the changes-requested PR has requested changes from a reviewer label Dec 16, 2025
@github-actions github-actions bot added tests: passed Django tests passed and removed tests: failed Django tests failed labels Dec 16, 2025
Copy link
Contributor

@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

♻️ Duplicate comments (1)
website/views/user.py (1)

1745-1745: Verify the email template variable name.

The context dictionary uses "action_url" as the key, but a past review flagged that the new_chat.html template expects "chat_url". If this mismatch exists, email links will be broken.

Run this script to check what variables the template actually expects:

#!/bin/bash
# Check what variables are used in the new_chat.html email template

rg -n "chat_url|action_url" --type=html
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 62e6ae0 and d9941a7.

📒 Files selected for processing (1)
  • website/views/user.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
website/views/user.py (1)
website/models.py (2)
  • ChatRequest (3091-3105)
  • Thread (3066-3071)
🔇 Additional comments (3)
website/views/user.py (3)

1719-1723: LGTM: Proper endpoint protection.

The @require_POST decorator correctly restricts this endpoint to POST requests only, and @login_required ensures authentication. This is a good security practice for state-changing operations.


1728-1729: LGTM: Self-messaging validation.

Correctly prevents users from creating threads with themselves, returning an appropriate 400 error.


1770-1782: LGTM: Thread creation logic.

The unlocked recipient flow correctly finds or creates a thread between the two participants and returns the appropriate JSON response.

@github-actions github-actions bot added tests: failed Django tests failed and removed tests: passed Django tests passed labels Dec 16, 2025
@e-esakman e-esakman marked this pull request as draft December 16, 2025 08:41
@github-actions github-actions bot added files-changed: 5 PR changes 5 files and removed files-changed: 7 PR changes 7 files labels Dec 16, 2025
@e-esakman e-esakman marked this pull request as ready for review December 16, 2025 08:52
@github-actions github-actions bot added tests: passed Django tests passed last-active: 0d PR last updated 0 days ago and removed tests: failed Django tests failed labels Dec 16, 2025
@github-actions
Copy link
Contributor

💬 Reminder: Unresolved Conversations

Hi @e-esakman!

This pull request has 1 unresolved conversation that need to be addressed.

Please review and resolve the pending discussions so we can move forward with merging this PR.

Thank you! 🙏

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

Labels

changes-requested PR has requested changes from a reviewer files-changed: 5 PR changes 5 files has-peer-review PR has received peer review last-active: 0d PR last updated 0 days ago migrations PR contains database migration files pre-commit: passed Pre-commit checks passed quality: high quality: medium tests: passed Django tests passed

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

When someone starts an encrypted chat send the recipient an email with a link to open it

3 participants