diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-targeting-section.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-targeting-section.png index 911eaae6e5196..5c9fabce55126 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-targeting-section.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-targeting-section.png differ diff --git a/frontend/src/lib/components/JSSnippet.tsx b/frontend/src/lib/components/JSSnippet.tsx index 1f0751187ef7e..3d13f23c49c95 100644 --- a/frontend/src/lib/components/JSSnippet.tsx +++ b/frontend/src/lib/components/JSSnippet.tsx @@ -8,7 +8,9 @@ export function JSSnippet(): JSX.Element { return ( {``} ) } diff --git a/frontend/src/scenes/authentication/redirectToLoggedInInstance.test.ts b/frontend/src/scenes/authentication/redirectToLoggedInInstance.test.ts index ac3c39b3c340f..df01ffd8a0f7b 100644 --- a/frontend/src/scenes/authentication/redirectToLoggedInInstance.test.ts +++ b/frontend/src/scenes/authentication/redirectToLoggedInInstance.test.ts @@ -7,12 +7,13 @@ describe('redirectToLoggedInInstance', () => { ['handles the sneaky string', ' ', null], ['handles not URLs', 'yo ho ho', null], ['handles EU', 'https://eu.posthog.com', 'eu'], - ['handles US', 'https://app.posthog.com', 'app'], + ['handles app', 'https://app.posthog.com', null], + ['handles US', 'https://us.posthog.com', 'us'], ['handles leading quotes', '"https://eu.posthog.com', 'eu'], ['handles trailing quotes', 'https://eu.posthog.com"', 'eu'], ['handles wrapping quotes', '"https://eu.posthog.com"', 'eu'], - ['handles ports', 'https://app.posthog.com:8123', 'app'], - ['handles longer urls', 'https://app.posthog.com:1234?query=parameter#hashParam', 'app'], + ['handles ports', 'https://us.posthog.com:8123', 'us'], + ['handles longer urls', 'https://eu.posthog.com:1234?query=parameter#hashParam', 'eu'], ])('%s', (_name, cookie, expected) => { expect(cleanedCookieSubdomain(cookie)).toEqual(expected) }) diff --git a/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts b/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts index b5e5bf852f8ec..401ec2dbc00e6 100644 --- a/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts +++ b/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts @@ -30,7 +30,7 @@ const PH_CURRENT_INSTANCE = 'ph_current_instance' const REDIRECT_TIMEOUT = 2500 -type Subdomain = 'eu' | 'app' +type Subdomain = 'eu' | 'us' export function cleanedCookieSubdomain(loggedInInstance: string | null): Subdomain | null { try { @@ -42,10 +42,10 @@ export function cleanedCookieSubdomain(loggedInInstance: string | null): Subdoma // convert to URL, so that we can be sure we're dealing with a valid URL const hostname = new URL(url).hostname switch (hostname) { - case 'app.posthog.com': - return 'app' case 'eu.posthog.com': return 'eu' + case 'us.posthog.com': + return 'us' default: return null } @@ -58,7 +58,7 @@ export function cleanedCookieSubdomain(loggedInInstance: string | null): Subdoma function regionFromSubdomain(subdomain: Subdomain): 'EU' | 'US' { switch (subdomain) { - case 'app': + case 'us': return 'US' case 'eu': return 'EU' diff --git a/posthog/settings/dynamic_settings.py b/posthog/settings/dynamic_settings.py index b640cc25e50c1..734c730d47404 100644 --- a/posthog/settings/dynamic_settings.py +++ b/posthog/settings/dynamic_settings.py @@ -188,6 +188,11 @@ "The number of rows that the heatmap query tries to sample.", int, ), + "REDIRECT_APP_TO_US": ( + get_from_env("REDIRECT_APP_TO_US", False, type_cast=str_to_bool), + "Temporary option to redirect all app traffic from app.posthog.com to us.posthog.com.", + bool, + ), } SETTINGS_ALLOWING_API_OVERRIDE = ( @@ -225,6 +230,7 @@ "SENTRY_AUTH_TOKEN", "SENTRY_ORGANIZATION", "HEATMAP_SAMPLE_N", + "REDIRECT_APP_TO_US", ) # SECRET_SETTINGS can only be updated but will never be exposed through the API (we do store them plain text in the DB) diff --git a/posthog/urls.py b/posthog/urls.py index 3567c73271d70..57dfd0feca942 100644 --- a/posthog/urls.py +++ b/posthog/urls.py @@ -1,8 +1,9 @@ from typing import Any, Callable, List, Optional, cast +from posthog.models.instance_setting import get_instance_setting from urllib.parse import urlparse from django.conf import settings -from django.http import HttpRequest, HttpResponse, HttpResponseServerError +from django.http import HttpRequest, HttpResponse, HttpResponseServerError, HttpResponseRedirect from django.template import loader from django.urls import URLPattern, include, path, re_path from django.views.decorators.csrf import ( @@ -17,6 +18,7 @@ SpectacularSwaggerView, ) from revproxy.views import ProxyView +from django.utils.http import url_has_allowed_host_and_scheme from sentry_sdk import last_event_id from two_factor.urls import urlpatterns as tf_urls @@ -90,6 +92,10 @@ def handler500(request): @ensure_csrf_cookie def home(request, *args, **kwargs): + if request.get_host().split(":")[0] == "app.posthog.com" and get_instance_setting("REDIRECT_APP_TO_US"): + url = "https://us.posthog.com{}".format(request.get_full_path()) + if url_has_allowed_host_and_scheme(url, "us.posthog.com", True): + return HttpResponseRedirect(url) return render_template("index.html", request)