pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

send region to backend

Pas 94e4e930 6ed0192d

+137 -23
+44
src/backend/accounts/region.ts
··· 1 + import { ofetch } from "ofetch"; 2 + 3 + import { getAuthHeaders } from "@/backend/accounts/auth"; 4 + import { AccountWithToken } from "@/stores/auth"; 5 + 6 + export type Region = 7 + | "us-east" 8 + | "us-west" 9 + | "south" 10 + | "asia" 11 + | "europe" 12 + | "unknown"; 13 + 14 + export interface RegionResponse { 15 + region: Region; 16 + lastChecked: number; 17 + userPicked: boolean; 18 + } 19 + 20 + export async function updateRegion( 21 + url: string, 22 + account: AccountWithToken, 23 + region: Region, 24 + userPicked: boolean = false, 25 + ) { 26 + return ofetch<RegionResponse>(`/users/${account.userId}/region`, { 27 + method: "PUT", 28 + headers: getAuthHeaders(account.token), 29 + baseURL: url, 30 + body: { 31 + region, 32 + userPicked, 33 + lastChecked: Math.floor(Date.now() / 1000), 34 + }, 35 + }); 36 + } 37 + 38 + export async function getRegion(url: string, account: AccountWithToken) { 39 + return ofetch<RegionResponse>(`/users/${account.userId}/region`, { 40 + method: "GET", 41 + headers: getAuthHeaders(account.token), 42 + baseURL: url, 43 + }); 44 + }
+17 -2
src/pages/parts/admin/RegionSelectorPart.tsx
··· 1 + import { useState } from "react"; 2 + 3 + import { Region } from "@/backend/accounts/region"; 1 4 import { Dropdown } from "@/components/form/Dropdown"; 2 5 import { Box } from "@/components/layout/Box"; 3 6 import { Heading2 } from "@/components/utils/Text"; 4 - import { Region, useRegionStore } from "@/utils/detectRegion"; 7 + import { useRegionStore } from "@/utils/detectRegion"; 5 8 6 9 export function RegionSelectorPart() { 7 10 const { region, setRegion } = useRegionStore(); 11 + const [isUpdating, setIsUpdating] = useState(false); 8 12 9 13 const regionOptions = [ 10 14 { id: "us-east", name: "US East (Ohio)" }, ··· 14 18 { id: "europe", name: "Europe Central (London)" }, 15 19 ]; 16 20 21 + const handleRegionChange = async (item: { id: string; name: string }) => { 22 + setIsUpdating(true); 23 + try { 24 + await setRegion(item.id as Region, true); 25 + } catch (error) { 26 + console.error("Failed to update region:", error); 27 + } finally { 28 + setIsUpdating(false); 29 + } 30 + }; 31 + 17 32 return ( 18 33 <> 19 34 <Heading2 className="mb-8 mt-12">Region Selector</Heading2> ··· 33 48 regionOptions.find((r) => r.id === region)?.name || 34 49 "Unknown (US East)", 35 50 }} 36 - setSelectedItem={(item) => setRegion(item.id as Region, true)} 51 + setSelectedItem={handleRegionChange} 37 52 direction="up" 38 53 /> 39 54 </div>
+8 -6
src/pages/parts/settings/SetupPart.tsx
··· 19 19 import { conf } from "@/setup/config"; 20 20 import { useAuthStore } from "@/stores/auth"; 21 21 22 - const getRegion = (): string | null => { 22 + const getRegion = async (): Promise<string | null> => { 23 23 if (typeof window === "undefined") return null; 24 24 try { 25 25 const regionData = window.localStorage.getItem("__MW::region"); ··· 31 31 } 32 32 }; 33 33 34 - const getBaseUrl = (): string => { 35 - const region = getRegion(); 34 + const getBaseUrl = async (): Promise<string> => { 35 + const region = await getRegion(); 36 36 switch (region) { 37 37 case "us-east": 38 38 return "https://fed-api-east.pstream.org"; 39 39 case "us-west": 40 40 return "https://fed-api-west.pstream.org"; 41 - case "south-america": 41 + case "south": 42 42 return "https://fed-api-south.pstream.org"; 43 43 case "asia": 44 44 return "https://fed-api-asia.pstream.org"; 45 45 case "europe": 46 46 return "https://fed-api-europe.pstream.org"; 47 + case "unknown": 48 + return "https://fed-api-east.pstream.org"; 47 49 default: 48 - return "https://fed-api-east.pstream.org"; 50 + return ""; 49 51 } 50 52 }; 51 53 52 - const BASE_URL = getBaseUrl(); 54 + const BASE_URL = await getBaseUrl(); 53 55 54 56 const testUrl = "https://postman-echo.com/get"; 55 57 const febboxApiTestUrl = `${BASE_URL}/movie/tt13654226`;
+68 -15
src/utils/detectRegion.tsx
··· 1 1 import { create } from "zustand"; 2 2 import { persist } from "zustand/middleware"; 3 3 4 - export type Region = 5 - | "us-east" 6 - | "us-west" 7 - | "south" 8 - | "asia" 9 - | "europe" 10 - | "unknown"; 4 + import { Region, getRegion, updateRegion } from "@/backend/accounts/region"; 5 + import { conf } from "@/setup/config"; 6 + import { useAuthStore } from "@/stores/auth"; 11 7 12 8 interface RegionStore { 13 9 region: Region | null; ··· 22 18 region: null, 23 19 lastChecked: null, 24 20 userPicked: false, 25 - setRegion: (region, userPicked = false) => 26 - set({ region, lastChecked: Math.floor(Date.now() / 1000), userPicked }), 21 + setRegion: async (region, userPicked = false) => { 22 + const url = conf().BACKEND_URL; 23 + const account = useAuthStore.getState().account; 24 + 25 + if (url && account) { 26 + try { 27 + const response = await updateRegion( 28 + url, 29 + account, 30 + region, 31 + userPicked, 32 + ); 33 + set({ 34 + region: response.region, 35 + lastChecked: response.lastChecked, 36 + userPicked: response.userPicked, 37 + }); 38 + } catch (error) { 39 + console.error("Failed to update region:", error); 40 + } 41 + } else { 42 + set({ 43 + region, 44 + lastChecked: Math.floor(Date.now() / 1000), 45 + userPicked, 46 + }); 47 + } 48 + }, 27 49 }), 28 50 { 29 51 name: "__MW::region", ··· 82 104 return closestRegion; 83 105 } 84 106 107 + // 1. Check if user manually picked a region (highest priority) 108 + // 2. Check if we need to refresh the region 109 + // 3. If refresh needed: 110 + // a. Try to get fresh region from backend 111 + // b. If backend region is fresh, use it 112 + // c. If backend region is expired or unavailable, fall back to IP detection 113 + // 4. If no refresh needed, use existing region 114 + 85 115 export async function detectRegion(): Promise<Region> { 86 116 const store = useRegionStore.getState(); 117 + const url = conf().BACKEND_URL; 118 + const account = useAuthStore.getState().account; 87 119 88 120 // If user picked a region, always return that 89 121 if (store.userPicked && store.region) { 90 122 return store.region; 91 123 } 92 124 93 - // If we have a recent detection, return that 94 - if ( 95 - store.region && 96 - store.lastChecked && 97 - Math.floor(Date.now() / 1000) - store.lastChecked < 2592000 // 30 days in seconds 98 - ) { 125 + // Check if we need to refresh the region 126 + const needsRefresh = 127 + !store.region || 128 + !store.lastChecked || 129 + Math.floor(Date.now() / 1000) - store.lastChecked >= 2592000; // 30 days in seconds 130 + 131 + if (!needsRefresh && store.region) { 99 132 return store.region; 100 133 } 101 134 102 135 try { 136 + // Try to get fresh region from backend first 137 + if (url && account) { 138 + try { 139 + const response = await getRegion(url, account); 140 + // Only update if the backend has a fresh region 141 + if ( 142 + response.lastChecked && 143 + Math.floor(Date.now() / 1000) - response.lastChecked < 2592000 144 + ) { 145 + if (!store.userPicked) { 146 + store.setRegion(response.region, response.userPicked); 147 + } 148 + return response.region; 149 + } 150 + } catch (error) { 151 + console.warn("Failed to get region from backend:", error); 152 + } 153 + } 154 + 155 + // Fallback to IP-based detection 103 156 const response = await fetch("https://ipapi.co/json/"); 104 157 const data = await response.json(); 105 158