feat(marketing): exempt public blueprints from noindex + fix / route collision

- add_no_crawl_headers now skips marketing.*, legal.*, billing.success,
  static, and robots_txt endpoints via _is_public_indexable_endpoint
  helper; all other routes keep the X-Robots-Tag noindex header
- recordings.index drops @login_required and instead redirects
  anonymous users to marketing.landing, resolving the URL-map
  collision between recordings_bp and marketing_bp at "/"
- robots.txt rewritten: public marketing pages and /legal/* allowed,
  /api/, /admin, /account, /share/, /app/, /checkout, /login, /signup,
  /webhooks/ disallowed; Googlebot, Bingbot, ClaudeBot, GPTBot,
  PerplexityBot, Applebot explicitly allowed
- New tests/test_no_crawl_headers.py (14 tests) covers exemption
  helper + integration on /, /robots.txt, /static, /admin, /login
- New tests/test_marketing_root_redirect.py (4 tests) verifies
  anonymous users at / never get a /login redirect

Tests verified via AST + logic walkthrough; pytest blocked on Windows
by pre-existing fcntl import in src/init_db.py (B-1.2 limitation).
This commit is contained in:
Allison
2026-04-27 16:28:55 -04:00
parent 55ae09431d
commit 1071e56173
5 changed files with 299 additions and 54 deletions

View File

@@ -1357,8 +1357,20 @@ def reset_status(recording_id):
@recordings_bp.route('/')
@login_required
def index():
"""Root route handler.
Anonymous users are redirected to the marketing landing page so the
public site is reachable at "/". Authenticated users continue to see
the recordings dashboard (legacy Speakr UI).
Phase 1 of marketing redesign 2026 (B-1.3) replaced the previous
@login_required decorator with this inline check to resolve the route
collision between recordings_bp.index and marketing_bp.landing.
"""
if not current_user.is_authenticated:
return redirect(url_for('marketing.landing'))
# Check if user is a group admin
is_team_admin = GroupMembership.query.filter_by(
user_id=current_user.id,