Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite committed Jan 31, 2024
1 parent e12c5de commit f8bf3c7
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 81 deletions.
47 changes: 25 additions & 22 deletions src/decide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { STORED_GROUP_PROPERTIES_KEY, STORED_PERSON_PROPERTIES_KEY } from './con
import { _isUndefined } from './utils/type-utils'
import { logger } from './utils/logger'
import { window, document, assignableWindow } from './utils/globals'
import { RequestRouterTarget } from './utils/request-router'

export class Decide {
instance: PostHog
Expand Down Expand Up @@ -35,7 +36,7 @@ export class Decide {

const encoded_data = _base64Encode(json_data)
this.instance._send_request(
`${this.instance.config.api_host}/decide/?v=3`,
this.instance.requestRouter.endpointFor(RequestRouterTarget.DECIDE, '/decide/?v=3'),
{ data: encoded_data, verbose: true },
{ method: 'POST' },
(response) => this.parseDecideResponse(response as DecideResponse)
Expand Down Expand Up @@ -76,15 +77,18 @@ export class Decide {
const surveysGenerator = window?.extendPostHogWithSurveys

if (response['surveys'] && !surveysGenerator) {
loadScript(this.instance.config.api_host + `/static/surveys.js`, (err) => {
if (err) {
return logger.error(`Could not load surveys script`, err)
loadScript(
this.instance.requestRouter.endpointFor(RequestRouterTarget.ASSETS, '/static/surveys.js'),
(err) => {
if (err) {
return logger.error(`Could not load surveys script`, err)
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.extendPostHogWithSurveys(this.instance)
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.extendPostHogWithSurveys(this.instance)
})
)
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -95,25 +99,24 @@ export class Decide {
!!response['autocaptureExceptions'] &&
_isUndefined(exceptionAutoCaptureAddedToWindow)
) {
loadScript(this.instance.config.api_host + `/static/exception-autocapture.js`, (err) => {
if (err) {
return logger.error(`Could not load exception autocapture script`, err)
loadScript(
this.instance.requestRouter.endpointFor(RequestRouterTarget.ASSETS, '/static/exception-autocapture.js'),
(err) => {
if (err) {
return logger.error(`Could not load exception autocapture script`, err)
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.extendPostHogWithExceptionAutocapture(this.instance, response)
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.extendPostHogWithExceptionAutocapture(this.instance, response)
})
)
}

if (response['siteApps']) {
if (this.instance.config.opt_in_site_apps) {
const apiHost = this.instance.config.api_host
for (const { id, url } of response['siteApps']) {
const scriptUrl = [
apiHost,
apiHost[apiHost.length - 1] === '/' && url[0] === '/' ? url.substring(1) : url,
].join('')
const scriptUrl = this.instance.requestRouter.endpointFor(RequestRouterTarget.ASSETS, url)

assignableWindow[`__$$ph_site_app_${id}`] = this.instance

Expand Down
1 change: 0 additions & 1 deletion src/extensions/cloud.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/extensions/exception-autocapture/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isPrimitive } from './type-checking'

import { _isArray, _isObject, _isUndefined } from '../../utils/type-utils'
import { logger } from '../../utils/logger'
import { RequestRouterTarget } from 'utils/request-router'

const EXCEPTION_INGESTION_ENDPOINT = '/e/'

Expand Down Expand Up @@ -129,7 +130,7 @@ export class ExceptionObserver {

const propertiesToSend = { ...properties, ...errorProperties }

const posthogHost = this.instance.config.ui_host || this.instance.config.api_host
const posthogHost = this.instance.requestRouter.endpointFor(RequestRouterTarget.UI)
errorProperties.$exception_personURL = posthogHost + '/person/' + this.instance.get_distinct_id()

this.sendExceptionEvent(propertiesToSend)
Expand Down
19 changes: 13 additions & 6 deletions src/extensions/replay/sessionrecording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { assignableWindow, window } from '../../utils/globals'
import { buildNetworkRequestOptions } from './config'
import { isLocalhost } from '../../utils/request-utils'
import { userOptedOut } from '../../gdpr-utils'
import { RequestRouterTarget } from 'utils/request-router'

const BASE_ENDPOINT = '/s/'

Expand Down Expand Up @@ -412,13 +413,19 @@ export class SessionRecording {
// imported) or matches the requested recorder version, don't load script. Otherwise, remotely import
// recorder.js from cdn since it hasn't been loaded.
if (this.instance.__loaded_recorder_version !== this.recordingVersion) {
loadScript(this.instance.config.api_host + `/static/${recorderJS}?v=${Config.LIB_VERSION}`, (err) => {
if (err) {
return logger.error(`Could not load ${recorderJS}`, err)
}
loadScript(
this.instance.requestRouter.endpointFor(
RequestRouterTarget.ASSETS,
`/static/${recorderJS}?v=${Config.LIB_VERSION}`
),
(err) => {
if (err) {
return logger.error(`Could not load ${recorderJS}`, err)
}

this._onScriptLoaded()
})
this._onScriptLoaded()
}
)
} else {
this._onScriptLoaded()
}
Expand Down
10 changes: 7 additions & 3 deletions src/extensions/sentry-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* @param {string} [prefix] Optional: Url of a self-hosted sentry instance (default: https://sentry.io/organizations/)
*/

import { RequestRouterTarget } from '../utils/request-router'
import { PostHog } from '../posthog-core'

// NOTE - we can't import from @sentry/types because it changes frequently and causes clashes
Expand Down Expand Up @@ -64,8 +65,11 @@ export class SentryIntegration implements _SentryIntegration {
if (event.level !== 'error' || !_posthog.__loaded) return event
if (!event.tags) event.tags = {}

const host = _posthog.config.ui_host || _posthog.config.api_host
event.tags['PostHog Person URL'] = host + '/person/' + _posthog.get_distinct_id()
const personUrl = _posthog.requestRouter.endpointFor(
RequestRouterTarget.UI,
'/person/' + _posthog.get_distinct_id()
)
event.tags['PostHog Person URL'] = personUrl
if (_posthog.sessionRecordingStarted()) {
event.tags['PostHog Recording URL'] = _posthog.get_session_replay_url({ withTimestamp: true })
}
Expand All @@ -82,7 +86,7 @@ export class SentryIntegration implements _SentryIntegration {
// PostHog Exception Properties,
$exception_message: exceptions[0]?.value,
$exception_type: exceptions[0]?.type,
$exception_personURL: host + '/person/' + _posthog.get_distinct_id(),
$exception_personURL: personUrl,
// Sentry Exception Properties
$sentry_event_id: event.event_id,
$sentry_exception: event.exception,
Expand Down
10 changes: 5 additions & 5 deletions src/extensions/toolbar.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { _register_event, _try, loadScript } from '../utils'
import { PostHog } from '../posthog-core'
import { DecideResponse, ToolbarParams } from '../types'
import { POSTHOG_MANAGED_HOSTS } from './cloud'
import { _getHashParam } from '../utils/request-utils'
import { logger } from '../utils/logger'
import { window, document, assignableWindow } from '../utils/globals'
import { RequestRouterTarget } from 'utils/request-router'

// TRICKY: Many web frameworks will modify the route on load, potentially before posthog is initialized.
// To get ahead of this we grab it as soon as the posthog-js is parsed
Expand Down Expand Up @@ -126,17 +126,17 @@ export class Toolbar {
// only load the toolbar once, even if there are multiple instances of PostHogLib
assignableWindow['_postHogToolbarLoaded'] = true

const host = this.instance.config.api_host
const host = this.instance.requestRouter.endpointFor(RequestRouterTarget.ASSETS)
// toolbar.js is served from the PostHog CDN, this has a TTL of 24 hours.
// the toolbar asset includes a rotating "token" that is valid for 5 minutes.
const fiveMinutesInMillis = 5 * 60 * 1000
// this ensures that we bust the cache periodically
const timestampToNearestFiveMinutes = Math.floor(Date.now() / fiveMinutesInMillis) * fiveMinutesInMillis
const toolbarUrl = `${host}${host.endsWith('/') ? '' : '/'}static/toolbar.js?t=${timestampToNearestFiveMinutes}`
const toolbarUrl = `${host}/static/toolbar.js?t=${timestampToNearestFiveMinutes}`
const disableToolbarMetrics =
!POSTHOG_MANAGED_HOSTS.includes(this.instance.config.api_host) &&
this.instance.config.advanced_disable_toolbar_metrics
this.instance.requestRouter.region === 'custom' && this.instance.config.advanced_disable_toolbar_metrics

// TODO: Dos the toolbar use the ingestion APIs?
const toolbarParams = {
token: this.instance.config.token,
...params,
Expand Down
19 changes: 8 additions & 11 deletions src/posthog-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { compressData, decideCompression } from './compression'
import { addParamsToURL, encodePostData, xhr } from './send-request'
import { RetryQueue } from './retry-queue'
import { SessionIdManager } from './sessionid'
import { RequestRouter } from './utils/request-router'
import { RequestRouter, RequestRouterTarget } from './utils/request-router'
import {
AutocaptureConfig,
CaptureOptions,
Expand Down Expand Up @@ -912,7 +912,12 @@ export class PostHog {
logger.info('send', data)
const jsonData = JSON.stringify(data)

const url = this.config.api_host + (options.endpoint || this.analyticsDefaultEndpoint)
// TODO: This doesn't really work 🤔 as the other endpoints call this with "options.endpoint"
// which now needs to be a complete override
const url = this.requestRouter.endpointFor(
RequestRouterTarget.CAPTURE_EVENTS,
options.endpoint || this.analyticsDefaultEndpoint
)

const has_unique_traits = options !== __NOOPTIONS

Expand Down Expand Up @@ -1530,9 +1535,8 @@ export class PostHog {
if (!this.sessionManager) {
return ''
}
const host = this.config.ui_host || this.config.api_host
const { sessionId, sessionStartTimestamp } = this.sessionManager.checkAndGetSessionAndWindowId(true)
let url = host + '/replay/' + sessionId
let url = this.requestRouter.endpointFor(RequestRouterTarget.UI, '/replay/' + sessionId)
if (options?.withTimestamp && sessionStartTimestamp) {
const LOOK_BACK = options.timestampLookBack ?? 10
if (!sessionStartTimestamp) {
Expand Down Expand Up @@ -1739,13 +1743,6 @@ export class PostHog {
this.config.disable_persistence = this.config.disable_cookie
}

// We assume the api_host is without a trailing slash in most places throughout the codebase
this.config.api_host = this.config.api_host.replace(/\/$/, '')

// us.posthog.com is only for the web app, so we don't allow that to be used as a capture endpoint
if (this.config.api_host === 'https://us.posthog.com') {
this.config.api_host = 'https://app.posthog.com'
}
this.persistence?.update_config(this.config)
this.sessionPersistence?.update_config(this.config)

Expand Down
8 changes: 6 additions & 2 deletions src/posthog-featureflags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {

import { _isArray } from './utils/type-utils'
import { logger } from './utils/logger'
import { RequestRouterTarget } from './utils/request-router'

const PERSISTENCE_ACTIVE_FEATURE_FLAGS = '$active_feature_flags'
const PERSISTENCE_OVERRIDE_FEATURE_FLAGS = '$override_feature_flags'
Expand Down Expand Up @@ -190,7 +191,7 @@ export class PostHogFeatureFlags {

const encoded_data = _base64Encode(json_data)
this.instance._send_request(
this.instance.config.api_host + '/decide/?v=3',
this.instance.requestRouter.endpointFor(RequestRouterTarget.DECIDE, '/decide/?v=3'),
{ data: encoded_data },
{ method: 'POST' },
this.instance._prepare_callback((response) => {
Expand Down Expand Up @@ -357,7 +358,10 @@ export class PostHogFeatureFlags {

if (!existing_early_access_features || force_reload) {
this.instance._send_request(
`${this.instance.config.api_host}/api/early_access_features/?token=${this.instance.config.token}`,
this.instance.requestRouter.endpointFor(
RequestRouterTarget.ASSETS,
`/api/early_access_features/?token=${this.instance.config.token}`
),
{},
{ method: 'GET' },
(response) => {
Expand Down
6 changes: 5 additions & 1 deletion src/posthog-surveys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SURVEYS } from './constants'
import { SurveyCallback, SurveyUrlMatchType } from './posthog-surveys-types'
import { _isUrlMatchingRegex } from './utils/request-utils'
import { window, document } from './utils/globals'
import { RequestRouterTarget } from 'utils/request-router'

export const surveyUrlValidationMap: Record<SurveyUrlMatchType, (conditionsUrl: string) => boolean> = {
icontains: (conditionsUrl) =>
Expand All @@ -22,7 +23,10 @@ export class PostHogSurveys {
const existingSurveys = this.instance.get_property(SURVEYS)
if (!existingSurveys || forceReload) {
this.instance._send_request(
`${this.instance.config.api_host}/api/surveys/?token=${this.instance.config.token}`,
this.instance.requestRouter.endpointFor(
RequestRouterTarget.ASSETS,
`/api/surveys/?token=${this.instance.config.token}`
),
{},
{ method: 'GET' },
(response) => {
Expand Down
55 changes: 26 additions & 29 deletions src/utils/request-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { PostHog } from '../posthog-core'
*/

export enum RequestRouterRegion {
US,
EU,
CUSTOM,
US = 'us',
EU = 'eu',
CUSTOM = 'custom',
}

export enum RequestRouterTarget {
UI,
CAPTURE_EVENTS,
CAPTURE_REPLAY,
DECIDE,
API,
UI = 'ui',
CAPTURE_EVENTS = 'capture_events',
CAPTURE_REPLAY = 'capture_replay',
DECIDE = 'decide',
ASSETS = 'assets',
}

// DEV NOTES:
Expand Down Expand Up @@ -45,32 +45,29 @@ export class RequestRouter {
}
}

endpointFor(target: RequestRouterTarget) {
let domainSuffix: string | undefined
let path: string | undefined
endpointFor(target: RequestRouterTarget, path: string = '') {
const uiHost = this.instance.config.ui_host || this.instance.config.api_host

if (target === RequestRouterTarget.UI) {
return uiHost
}

if (this.region === RequestRouterRegion.CUSTOM) {
return this.instance.config.api_host
}

const suffix = 'i.posthog.com' + path

switch (target) {
case RequestRouterTarget.UI:
domainSuffix = 'app'
break
case RequestRouterTarget.CAPTURE_EVENTS:
domainSuffix = 'e'
path = 'e/'
break
return `https://${this.region}-c.${suffix}`
case RequestRouterTarget.CAPTURE_REPLAY:
domainSuffix = 'r'
path = 'r/'
break
return `https://${this.region}-s.${suffix}`
case RequestRouterTarget.DECIDE:
domainSuffix = 'decide'
path = 'decide/'
break
case RequestRouterTarget.API:
domainSuffix = 'api'
path = 'api/'
break
return `https://${this.region}-d.${suffix}`
case RequestRouterTarget.ASSETS:
// TODO: Is this right? This would be all things like surveys / early access requests
return `https://${this.region}.${suffix}`
}

return this.config.api_host + (options.endpoint || this.analyticsDefaultEndpoint)
}
}

0 comments on commit f8bf3c7

Please sign in to comment.