@@ -480,4 +480,6 @@ export type MetricEvents = {
|
|||||||
* Specifically for the `BlockedGeoOverlay`
|
* Specifically for the `BlockedGeoOverlay`
|
||||||
*/
|
*/
|
||||||
'blockedGeoOverlay:shown': {}
|
'blockedGeoOverlay:shown': {}
|
||||||
|
|
||||||
|
'geo:debug': {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import {networkRetry} from '#/lib/async/retry'
|
|||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {type Device, device} from '#/storage'
|
import {type Device, device} from '#/storage'
|
||||||
|
|
||||||
|
const IPCC_URL = `https://bsky.app/ipcc`
|
||||||
|
const BAPP_CONFIG_URL = `https://bapp-config.bsky.workers.dev/config`
|
||||||
|
|
||||||
const events = new EventEmitter()
|
const events = new EventEmitter()
|
||||||
const EVENT = 'geolocation-updated'
|
const EVENT = 'geolocation-updated'
|
||||||
const emitGeolocationUpdate = (geolocation: Device['geolocation']) => {
|
const emitGeolocationUpdate = (geolocation: Device['geolocation']) => {
|
||||||
@@ -29,8 +32,18 @@ export const DEFAULT_GEOLOCATION: Device['geolocation'] = {
|
|||||||
isAgeRestrictedGeo: false,
|
isAgeRestrictedGeo: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGeolocation(): Promise<Device['geolocation']> {
|
function sanitizeGeolocation(
|
||||||
const res = await fetch(`https://bsky.app/ipcc`)
|
geolocation: Device['geolocation'],
|
||||||
|
): Device['geolocation'] {
|
||||||
|
return {
|
||||||
|
countryCode: geolocation?.countryCode ?? undefined,
|
||||||
|
isAgeBlockedGeo: geolocation?.isAgeBlockedGeo ?? false,
|
||||||
|
isAgeRestrictedGeo: geolocation?.isAgeRestrictedGeo ?? false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGeolocation(url: string): Promise<Device['geolocation']> {
|
||||||
|
const res = await fetch(url)
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error(`geolocation: lookup failed ${res.status}`)
|
throw new Error(`geolocation: lookup failed ${res.status}`)
|
||||||
@@ -43,12 +56,39 @@ async function getGeolocation(): Promise<Device['geolocation']> {
|
|||||||
countryCode: json.countryCode,
|
countryCode: json.countryCode,
|
||||||
isAgeBlockedGeo: json.isAgeBlockedGeo ?? false,
|
isAgeBlockedGeo: json.isAgeBlockedGeo ?? false,
|
||||||
isAgeRestrictedGeo: json.isAgeRestrictedGeo ?? false,
|
isAgeRestrictedGeo: json.isAgeRestrictedGeo ?? false,
|
||||||
|
// @ts-ignore
|
||||||
|
regionCode: json.regionCode ?? undefined,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function compareWithIPCC(bapp: Device['geolocation']) {
|
||||||
|
try {
|
||||||
|
const ipcc = await getGeolocation(IPCC_URL)
|
||||||
|
|
||||||
|
if (!ipcc || !bapp) return
|
||||||
|
|
||||||
|
logger.metric(
|
||||||
|
'geo:debug',
|
||||||
|
{
|
||||||
|
bappCountryCode: bapp.countryCode,
|
||||||
|
// @ts-ignore
|
||||||
|
bappRegionCode: bapp.regionCode,
|
||||||
|
bappIsAgeBlockedGeo: bapp.isAgeBlockedGeo,
|
||||||
|
bappIsAgeRestrictedGeo: bapp.isAgeRestrictedGeo,
|
||||||
|
ipccCountryCode: ipcc.countryCode,
|
||||||
|
ipccIsAgeBlockedGeo: ipcc.isAgeBlockedGeo,
|
||||||
|
ipccIsAgeRestrictedGeo: ipcc.isAgeRestrictedGeo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statsig: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local promise used within this file only.
|
* Local promise used within this file only.
|
||||||
*/
|
*/
|
||||||
@@ -81,11 +121,12 @@ export function beginResolveGeolocation() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Try once, fail fast
|
// Try once, fail fast
|
||||||
const geolocation = await getGeolocation()
|
const geolocation = await getGeolocation(BAPP_CONFIG_URL)
|
||||||
if (geolocation) {
|
if (geolocation) {
|
||||||
device.set(['geolocation'], geolocation)
|
device.set(['geolocation'], sanitizeGeolocation(geolocation))
|
||||||
emitGeolocationUpdate(geolocation)
|
emitGeolocationUpdate(geolocation)
|
||||||
logger.debug(`geolocation: success`, {geolocation})
|
logger.debug(`geolocation: success`, {geolocation})
|
||||||
|
compareWithIPCC(geolocation)
|
||||||
} else {
|
} else {
|
||||||
// endpoint should throw on all failures, this is insurance
|
// endpoint should throw on all failures, this is insurance
|
||||||
throw new Error(`geolocation: nothing returned from initial request`)
|
throw new Error(`geolocation: nothing returned from initial request`)
|
||||||
@@ -101,13 +142,14 @@ export function beginResolveGeolocation() {
|
|||||||
device.set(['geolocation'], DEFAULT_GEOLOCATION)
|
device.set(['geolocation'], DEFAULT_GEOLOCATION)
|
||||||
|
|
||||||
// retry 3 times, but don't await, proceed with default
|
// retry 3 times, but don't await, proceed with default
|
||||||
networkRetry(3, getGeolocation)
|
networkRetry(3, () => getGeolocation(BAPP_CONFIG_URL))
|
||||||
.then(geolocation => {
|
.then(geolocation => {
|
||||||
if (geolocation) {
|
if (geolocation) {
|
||||||
device.set(['geolocation'], geolocation)
|
device.set(['geolocation'], sanitizeGeolocation(geolocation))
|
||||||
emitGeolocationUpdate(geolocation)
|
emitGeolocationUpdate(geolocation)
|
||||||
logger.debug(`geolocation: success`, {geolocation})
|
logger.debug(`geolocation: success`, {geolocation})
|
||||||
success = true
|
success = true
|
||||||
|
compareWithIPCC(geolocation)
|
||||||
} else {
|
} else {
|
||||||
// endpoint should throw on all failures, this is insurance
|
// endpoint should throw on all failures, this is insurance
|
||||||
throw new Error(`geolocation: nothing returned from retries`)
|
throw new Error(`geolocation: nothing returned from retries`)
|
||||||
|
|||||||
Reference in New Issue
Block a user