proxy SD country list fetch through backend to fix CORS failure

This commit is contained in:
Seth Van Niekerk 2026-06-10 08:28:59 -04:00
parent d3ecefb7d6
commit 887a177581
No known key found for this signature in database
GPG key ID: E86ACA677312A675
4 changed files with 45 additions and 13 deletions

View file

@ -53,7 +53,7 @@ class EPGSourceViewSet(viewsets.ModelViewSet):
try:
return [perm() for perm in permission_classes_by_action[self.action]]
except KeyError:
if self.action in ('sd_lineups', 'sd_lineups_search'):
if self.action in ('sd_lineups', 'sd_lineups_search', 'sd_countries'):
if self.request.method == 'GET':
return [IsStandardUser()]
return [IsAdmin()]
@ -390,6 +390,31 @@ class EPGSourceViewSet(viewsets.ModelViewSet):
status=status.HTTP_502_BAD_GATEWAY
)
@action(detail=True, methods=["get"], url_path="sd-countries")
def sd_countries(self, request, pk=None):
"""Proxy /available/countries from the SD API to avoid browser CORS restrictions."""
import requests as http_requests
from apps.epg.tasks import SD_BASE_URL
source = self.get_object()
if source.source_type != 'schedules_direct':
return Response(
{"error": "This action is only available for Schedules Direct sources."},
status=status.HTTP_400_BAD_REQUEST,
)
try:
resp = http_requests.get(
f"{SD_BASE_URL}/available/countries",
timeout=15,
)
resp.raise_for_status()
return Response(resp.json())
except http_requests.exceptions.RequestException as e:
return Response(
{"error": f"Failed to fetch countries: {str(e)}"},
status=status.HTTP_502_BAD_GATEWAY,
)
@action(detail=True, methods=["post"], url_path="sd-lineups/search")
def sd_lineups_search(self, request, pk=None):
"""

View file

@ -3889,6 +3889,14 @@ export default class API {
}
}
static async getSDCountries(sourceId) {
try {
return await request(`${host}/api/epg/sources/${sourceId}/sd-countries/`);
} catch (e) {
return null;
}
}
static async getSDLineups(sourceId) {
try {
const response = await request(

View file

@ -325,18 +325,16 @@ const SDLineupManager = ({ sourceId }) => {
useEffect(() => {
if (sourceId) {
fetchActiveLineups();
// Fetch country list from SD API per their recommendation to not hardcode
fetch('https://json.schedulesdirect.org/20141201/available/countries')
.then((r) => r.json())
.then((data) => {
const all = Object.values(data).flat();
const mapped = all
.filter((c) => c.shortName && c.fullName)
.map((c) => ({ value: c.shortName, label: c.fullName }))
.sort((a, b) => a.label.localeCompare(b.label));
if (mapped.length > 0) setCountries(mapped);
})
.catch(() => {}); // fallback list remains if fetch fails
// Fetch country list via backend proxy to avoid browser CORS restrictions
API.getSDCountries(sourceId).then((data) => {
if (!data) return;
const all = Object.values(data).flat();
const mapped = all
.filter((c) => c.shortName && c.fullName)
.map((c) => ({ value: c.shortName, label: c.fullName }))
.sort((a, b) => a.label.localeCompare(b.label));
if (mapped.length > 0) setCountries(mapped);
});
}
}, [sourceId, fetchActiveLineups]);

View file

@ -8,6 +8,7 @@ global.fetch = vi.fn().mockResolvedValue({
vi.mock('../../../api.js', () => ({
default: {
getSDCountries: vi.fn().mockResolvedValue({}),
getSDLineups: vi.fn().mockResolvedValue([]),
addSDLineup: vi.fn().mockResolvedValue({ success: true }),
deleteSDLineup: vi.fn().mockResolvedValue({ success: true }),