Skip to content

feat: add signup pause flag#249

Merged
gregagi merged 3 commits into
mainfrom
forge/allow-signups-flag
May 9, 2026
Merged

feat: add signup pause flag#249
gregagi merged 3 commits into
mainfrom
forge/allow-signups-flag

Conversation

@gregagi
Copy link
Copy Markdown
Collaborator

@gregagi gregagi commented May 9, 2026

Summary

  • add ALLOW_SIGNUPS env flag (defaults to true) for pausing new registrations
  • gate both email/password and social-account auto-signups through the same setting
  • add a closed signup page that points existing users to login
  • add regression coverage for the adapters and closed-state template
  • update CHANGELOG.md

Verification

  • uv run pytest core/tests/test_signup_gating.py core/tests/test_forms.py::TestCustomSignUpFormTurnstile -q
  • uv run --with ruff ruff check tuxseo/settings.py core/adapters.py core/tests/test_signup_gating.py
  • uv run --with ruff ruff format --check tuxseo/settings.py core/adapters.py core/tests/test_signup_gating.py

Note: full DB-backed signup page test could not run in this container because the configured Postgres host db is not resolvable here.

Summary by CodeRabbit

  • New Features
    • Added ALLOW_SIGNUPS configuration option to pause new user registrations while keeping existing users able to log in. A signups-closed page guides users accordingly.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

Warning

Rate limit exceeded

@gregagi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 47 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: abba46f6-d565-4dc9-977d-8e3c47527fa8

📥 Commits

Reviewing files that changed from the base of the PR and between 96b6319 and c063a3d.

📒 Files selected for processing (3)
  • core/adapters.py
  • core/tests/test_signup_gating.py
  • frontend/templates/account/signup_closed.html
📝 Walkthrough

Walkthrough

This PR introduces signup gating for TuxSEO via a new ALLOW_SIGNUPS environment flag that defaults to true. Both standard and social account signup are blocked when disabled, while existing user logins remain available. A new signup_closed.html template informs users of the pause and directs them to login instead.

Changes

Signup Gating Feature

Layer / File(s) Summary
Settings Configuration
tuxseo/settings.py
ALLOW_SIGNUPS environment variable (boolean, default True) is introduced; import refactoring for tuxseo.posthog_logs is included.
Adapter Implementations
core/adapters.py
CustomAccountAdapter.is_open_for_signup(request) and CustomSocialAccountAdapter.is_open_for_signup(request, sociallogin) return settings.ALLOW_SIGNUPS to gate email and social signup flows.
Signup Closed Template
frontend/templates/account/signup_closed.html
New template extends base_landing.html, displays "Signups paused" message, and provides a login link to direct existing users.
Tests & Documentation
core/tests/test_signup_gating.py, CHANGELOG.md
Four tests verify adapter behavior under different ALLOW_SIGNUPS settings and validate template content; CHANGELOG documents the new flag.

Sequence Diagram

sequenceDiagram
    participant User
    participant SignupFlow
    participant Adapter
    participant Settings
    participant ClosedTemplate
    User->>SignupFlow: Attempt to register
    SignupFlow->>Adapter: is_open_for_signup(request)?
    Adapter->>Settings: Check ALLOW_SIGNUPS value
    Settings-->>Adapter: False
    Adapter-->>SignupFlow: Signup blocked
    SignupFlow->>ClosedTemplate: Render signup_closed.html
    ClosedTemplate-->>User: Display "Signups paused" + login link
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • rasulkireev/TuxSEO#81: Modifies CustomSocialAccountAdapter class similarly to this PR, adding new adapter methods that affect signup/account behavior.

Poem

🐰 The gates of signup close today,
Yet login paths still light the way,
With adapters swift and settings true,
New accounts pause, old ones renew!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add signup pause flag' accurately describes the main objective of the PR, which is to introduce an ALLOW_SIGNUPS environment flag to control signup acceptance.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch forge/allow-signups-flag

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 9, 2026

Greptile Summary

This PR introduces an ALLOW_SIGNUPS environment flag (defaulting to true) that lets operators pause new registrations without disrupting existing user logins. Both the email/password and social-account adapters are gated through the same setting, a matching signup_closed.html template is added, and lightweight unit tests cover the open/closed states.

  • Adapter gating: is_open_for_signup is added to both CustomAccountAdapter and CustomSocialAccountAdapter, reading directly from settings.ALLOW_SIGNUPS; neither calls super(), which silently bypasses any allauth-level signup guard that may exist now or in the future.
  • Settings placement: ALLOW_SIGNUPS is added via env.bool() with a safe default of True and sits alongside the other allauth settings.
  • Template: signup_closed.html extends base_landing.html and cleanly redirects users to the login page; the filesystem-based test validates the expected copy and absence of form fields.

Confidence Score: 4/5

Safe to merge; the flag defaults to open, so existing behaviour is unchanged until an operator explicitly sets ALLOW_SIGNUPS=false.

Both adapter overrides return settings.ALLOW_SIGNUPS directly without chaining to super(). If allauth ever adds conditions of its own to is_open_for_signup, those checks would be silently bypassed when ALLOW_SIGNUPS=True. The closed path works correctly and the template is well-formed; the risk is confined to edge cases in the open path.

core/adapters.py — both is_open_for_signup methods skip super().

Important Files Changed

Filename Overview
core/adapters.py Adds is_open_for_signup to both account and social adapters, reading settings.ALLOW_SIGNUPS; both skip super() which could bypass future allauth-level guards.
tuxseo/settings.py Adds ALLOW_SIGNUPS = env.bool("ALLOW_SIGNUPS", default=True) near the allauth block; import formatting fixed for posthog_logs imports.
core/tests/test_signup_gating.py New test file covering open/closed states for both adapters and a filesystem-based check of the closed template; missing an explicit open-state test for the social adapter.
frontend/templates/account/signup_closed.html New allauth-standard signup_closed.html template extending base_landing.html; clean Tailwind layout directing users to login.
CHANGELOG.md Adds ALLOW_SIGNUPS entry to the Unreleased section.

Reviews (1): Last reviewed commit: "feat: add signup pause flag" | Re-trigger Greptile

Comment thread core/adapters.py Outdated
Comment on lines +43 to +45
def is_open_for_signup(self, request):
"""Allow operators to pause new registrations without affecting existing users."""
return settings.ALLOW_SIGNUPS
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Both is_open_for_signup overrides return settings.ALLOW_SIGNUPS directly without calling super(). If a future version of allauth (or an intermediate custom adapter) adds additional conditions to is_open_for_signup — such as a max-user cap, IP-based blocks, or another admin flag — those checks would be silently bypassed. Chaining to super() is the safe pattern here and costs nothing when ALLOW_SIGNUPS is True.

Suggested change
def is_open_for_signup(self, request):
"""Allow operators to pause new registrations without affecting existing users."""
return settings.ALLOW_SIGNUPS
def is_open_for_signup(self, request):
"""Allow operators to pause new registrations without affecting existing users."""
return settings.ALLOW_SIGNUPS and super().is_open_for_signup(request)

Comment thread core/adapters.py Outdated
Comment on lines +111 to +113
def is_open_for_signup(self, request, sociallogin):
"""Mirror email signup gating for social-account auto-signups."""
return settings.ALLOW_SIGNUPS
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Same super()-bypass concern as CustomAccountAdapter.is_open_for_signup. Chaining ensures any allauth-level or future adapter-level guard is still honoured.

Suggested change
def is_open_for_signup(self, request, sociallogin):
"""Mirror email signup gating for social-account auto-signups."""
return settings.ALLOW_SIGNUPS
def is_open_for_signup(self, request, sociallogin):
"""Mirror email signup gating for social-account auto-signups."""
return settings.ALLOW_SIGNUPS and super().is_open_for_signup(request, sociallogin)

Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
frontend/templates/account/signup_closed.html (1)

4-18: ⚡ Quick win

Use semantic page landmarks for the closed-signup view.

Line 4 and Line 5 currently use generic container divs; switching to main/section improves document semantics and accessibility with minimal change.

♻️ Proposed semantic structure update
-<div class="flex justify-center items-center px-4 py-16 my-4 sm:px-6 lg:px-8">
-  <div class="space-y-6 w-full max-w-md text-center">
-    <div>
+<main class="flex justify-center items-center px-4 py-16 my-4 sm:px-6 lg:px-8">
+  <section class="space-y-6 w-full max-w-md text-center" aria-labelledby="signup-closed-title">
+    <div>
       <p class="text-sm font-semibold tracking-wide text-gray-500 uppercase">Signups paused</p>
-      <h1 class="mt-3 text-3xl font-extrabold text-gray-900">TuxSEO is not accepting new accounts right now.</h1>
+      <h1 id="signup-closed-title" class="mt-3 text-3xl font-extrabold text-gray-900">TuxSEO is not accepting new accounts right now.</h1>
       <p class="mt-4 text-sm leading-6 text-gray-600">
         Existing users can continue using the product as usual. New account creation is paused for now.
       </p>
     </div>
     <a href="{% url 'account_login' %}"
        class="inline-flex justify-center px-5 py-3 text-sm font-semibold text-white bg-gray-800 rounded-md hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
       Log in to your account
     </a>
-  </div>
-</div>
+  </section>
+</main>

As per coding guidelines, "frontend/templates/**/*.html: Use semantic HTML elements (dialog, details/summary, etc.) in Django templates."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/templates/account/signup_closed.html` around lines 4 - 18, Replace
the two outer generic div containers with semantic landmarks: change the
outermost div (class="flex justify-center items-center px-4 py-16 my-4 sm:px-6
lg:px-8") to a <main> element, and change the inner div (class="space-y-6 w-full
max-w-md text-center") to a <section> with a descriptive accessible name (e.g.,
add role="region" and aria-labelledby pointing to the heading); add an id to the
existing <h1> (e.g., id="signup-closed-heading") and reference it from the
section's aria-labelledby, and keep all existing classes and the login anchor
({% url 'account_login' %}) unchanged so layout and links remain identical while
improving semantics and accessibility.
core/tests/test_signup_gating.py (1)

29-40: ⚡ Quick win

Prefer rendered-template assertions over raw-file string checks.

The test reads template source directly via read_text(), so template rendering failures (e.g., broken {% url %} tags or syntax errors) would not be caught. Use render_to_string() to validate that the template compiles and renders correctly. Note: The template appears to be unused—no view currently renders signup_closed.html when signups close; consider whether this template is reachable or if it should be wired into the signup flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/tests/test_signup_gating.py` around lines 29 - 40, The test
test_signup_closed_template_tells_existing_users_to_log_in reads the template
file source directly which won’t catch template rendering/compilation errors;
change it to render the template using Django's render_to_string (import
render_to_string) for "account/signup_closed.html" and run assertions against
the rendered string (keep the same assertions for "Signups paused", "Existing
users can continue using the product as usual", "account_login", and absence of
'name="email"' and 'name="password1"'); also note the template may be unused—add
a brief check or TODO to ensure signup_closed.html is actually wired into the
signup flow or a view renders it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@core/tests/test_signup_gating.py`:
- Around line 29-40: The test
test_signup_closed_template_tells_existing_users_to_log_in reads the template
file source directly which won’t catch template rendering/compilation errors;
change it to render the template using Django's render_to_string (import
render_to_string) for "account/signup_closed.html" and run assertions against
the rendered string (keep the same assertions for "Signups paused", "Existing
users can continue using the product as usual", "account_login", and absence of
'name="email"' and 'name="password1"'); also note the template may be unused—add
a brief check or TODO to ensure signup_closed.html is actually wired into the
signup flow or a view renders it.

In `@frontend/templates/account/signup_closed.html`:
- Around line 4-18: Replace the two outer generic div containers with semantic
landmarks: change the outermost div (class="flex justify-center items-center
px-4 py-16 my-4 sm:px-6 lg:px-8") to a <main> element, and change the inner div
(class="space-y-6 w-full max-w-md text-center") to a <section> with a
descriptive accessible name (e.g., add role="region" and aria-labelledby
pointing to the heading); add an id to the existing <h1> (e.g.,
id="signup-closed-heading") and reference it from the section's aria-labelledby,
and keep all existing classes and the login anchor ({% url 'account_login' %})
unchanged so layout and links remain identical while improving semantics and
accessibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a621a84-9a7e-4c10-87f0-68e9b487c59f

📥 Commits

Reviewing files that changed from the base of the PR and between a1d1595 and 96b6319.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • core/adapters.py
  • core/tests/test_signup_gating.py
  • frontend/templates/account/signup_closed.html
  • tuxseo/settings.py

@gregagi gregagi merged commit b5465c4 into main May 9, 2026
4 checks passed
@gregagi gregagi deleted the forge/allow-signups-flag branch May 9, 2026 11:33
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.

1 participant