Block banned-country locations and align GraphQL ops
Defense-in-depth banned-country gate covering every entry point that could set a location Feeld's policy disallows (~60 countries from their support article): - New src/config/bannedCountries.ts — single source of truth (ISO codes + aliases) - New src/utils/reverseGeocode.ts — Nominatim reverse lookup w/ localStorage cache - New src/api/links/bannedCountryLink.ts — Apollo link chokepoint; intercepts every DeviceLocationUpdate mutation and refuses to forward if reverse-geocode resolves to a banned country. Catches Settings, Discover, Likes scanner, and ApiExplorer raw GraphQL alike. - useLocation.tsx — setLocation throws BannedCountryError; saveLocation gate; sanitize banned entries on localStorage and server hydration - Settings.tsx — block at search, saved-location pick, and save-current - Likes.tsx — skip banned saved locations in scanForLikes and "Fuck It" scan - server/index.js — PUT /api/saved-locations filters; readSavedLocations filters legacy banned entries so rotation cron is safe too - nginx.conf — route additions for new backend endpoints Plus the broader rc/realign-graphql-ops session work: GraphQL query/mutation realignment after Feeld API changes, ApiExplorer updates, Profile/Discover/Likes refinements, useFavorites hook, dataSync extensions, vite proxy adjustments. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,8 @@ import { useAuth } from '../hooks/useAuth';
|
||||
import { authManager } from '../api/auth';
|
||||
import type { AuthStatus } from '../api/auth';
|
||||
import { LoadingPage } from '../components/ui/Loading';
|
||||
import { useLocation, geocodeAddress } from '../hooks/useLocation';
|
||||
import { useLocation, geocodeAddress, BannedCountryError } from '../hooks/useLocation';
|
||||
import { isCountryBanned, findBannedCountry } from '../config/bannedCountries';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
// Inline styles for guaranteed rendering
|
||||
@@ -600,10 +601,20 @@ export function SettingsPage() {
|
||||
const result = await geocodeAddress(searchQuery);
|
||||
|
||||
if (result) {
|
||||
if (isCountryBanned(result.country, result.countryCode)) {
|
||||
const hit = findBannedCountry(result.country, result.countryCode);
|
||||
setSearchError(
|
||||
`Feeld is not available in ${hit?.name ?? result.country ?? 'this country'}. Using a location there will get your account flagged. Pick a different location.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const newLocation = {
|
||||
latitude: result.lat,
|
||||
longitude: result.lng,
|
||||
name: result.displayName,
|
||||
country: result.country,
|
||||
countryCode: result.countryCode,
|
||||
};
|
||||
setLocation(newLocation);
|
||||
|
||||
@@ -630,10 +641,22 @@ export function SettingsPage() {
|
||||
};
|
||||
|
||||
const handleSelectSavedLocation = async (saved: typeof savedLocations[0]) => {
|
||||
if (isCountryBanned(saved.country, saved.countryCode)) {
|
||||
const hit = findBannedCountry(saved.country, saved.countryCode);
|
||||
setLocationStatus({
|
||||
type: 'error',
|
||||
text: `Feeld is not available in ${hit?.name ?? saved.country ?? 'that country'}. Remove this saved location and pick a different one.`,
|
||||
});
|
||||
setTimeout(() => setLocationStatus(null), 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
const newLocation = {
|
||||
latitude: saved.latitude,
|
||||
longitude: saved.longitude,
|
||||
name: saved.name,
|
||||
country: saved.country,
|
||||
countryCode: saved.countryCode,
|
||||
};
|
||||
setLocation(newLocation);
|
||||
setLocationStatus(null);
|
||||
@@ -659,9 +682,24 @@ export function SettingsPage() {
|
||||
|
||||
const handleSaveCurrentLocation = () => {
|
||||
if (!location || !saveLocationName.trim()) return;
|
||||
saveLocation(saveLocationName.trim(), location.latitude, location.longitude);
|
||||
setSaveLocationName('');
|
||||
setShowSaveDialog(false);
|
||||
try {
|
||||
saveLocation(saveLocationName.trim(), location.latitude, location.longitude, {
|
||||
country: location.country,
|
||||
countryCode: location.countryCode,
|
||||
});
|
||||
setSaveLocationName('');
|
||||
setShowSaveDialog(false);
|
||||
} catch (err) {
|
||||
if (err instanceof BannedCountryError) {
|
||||
setLocationStatus({
|
||||
type: 'error',
|
||||
text: `Can't save — Feeld is not available in ${err.bannedCountry}.`,
|
||||
});
|
||||
setTimeout(() => setLocationStatus(null), 5000);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Desiring For options (from API LocalisedDesireCategory)
|
||||
|
||||
Reference in New Issue
Block a user