feat: support zero-config LAN access for local development
Auto-rewrite loopback API URLs to current page host in browser Allow CORS for private LAN IPs when localhost origin is configured Remove display limit for related Pokémon in detail view
This commit is contained in:
@@ -60,7 +60,32 @@ function normalizeApiBaseUrl(value: unknown): string | null {
|
||||
}
|
||||
|
||||
function activeApiBaseUrl(): string {
|
||||
return typeof window === 'undefined' ? serverApiBaseUrl : browserApiBaseUrl;
|
||||
return typeof window === 'undefined'
|
||||
? serverApiBaseUrl
|
||||
: resolveBrowserApiBaseUrl(browserApiBaseUrl);
|
||||
}
|
||||
|
||||
function resolveBrowserApiBaseUrl(value: string): string {
|
||||
if (typeof window === 'undefined') {
|
||||
return value;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(value, window.location.origin);
|
||||
|
||||
if (isLoopbackHost(url.hostname) && !isLoopbackHost(window.location.hostname)) {
|
||||
url.hostname = window.location.hostname;
|
||||
return url.toString().replace(/\/+$/, '');
|
||||
}
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function isLoopbackHost(hostname: string): boolean {
|
||||
return ['localhost', '127.0.0.1', '::1', '[::1]'].includes(hostname);
|
||||
}
|
||||
|
||||
export function readStoredLocale(): string {
|
||||
|
||||
@@ -45,7 +45,32 @@ function normalizeApiBaseUrl(value: unknown): string | null {
|
||||
}
|
||||
|
||||
function activeApiBaseUrl(): string {
|
||||
return typeof window === 'undefined' ? serverApiBaseUrl : browserApiBaseUrl;
|
||||
return typeof window === 'undefined'
|
||||
? serverApiBaseUrl
|
||||
: resolveBrowserApiBaseUrl(browserApiBaseUrl);
|
||||
}
|
||||
|
||||
function resolveBrowserApiBaseUrl(value: string): string {
|
||||
if (typeof window === 'undefined') {
|
||||
return value;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(value, window.location.origin);
|
||||
|
||||
if (isLoopbackHost(url.hostname) && !isLoopbackHost(window.location.hostname)) {
|
||||
url.hostname = window.location.hostname;
|
||||
return url.toString().replace(/\/+$/, '');
|
||||
}
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function isLoopbackHost(hostname: string): boolean {
|
||||
return ['localhost', '127.0.0.1', '::1', '[::1]'].includes(hostname);
|
||||
}
|
||||
|
||||
function apiUrl(path: string): string {
|
||||
|
||||
@@ -39,7 +39,6 @@ const tradingDraftItems = ref<Array<{ itemId: number; preference: TradingPrefere
|
||||
const tradingActiveItemIndex = ref(0);
|
||||
const timeOfDays = ['早晨', '中午', '傍晚', '晚上'];
|
||||
const weathers = ['晴天', '阴天', '雨天'];
|
||||
const relatedPokemonLimit = 6;
|
||||
const pokemonDetailRouteNames = new Set(['pokemon-detail', 'pokemon-edit']);
|
||||
|
||||
const { data: initialPokemon } = useAsyncData<PokemonDetail | null>(
|
||||
@@ -333,16 +332,14 @@ const relatedPokemonRows = computed(() => {
|
||||
const selectedTab = relatedHabitatTab.value || (pokemon.value ? habitatTabValue(pokemon.value.environment.id) : '');
|
||||
|
||||
if (selectedTab === 'all') {
|
||||
return rows.slice(0, relatedPokemonLimit);
|
||||
return rows;
|
||||
}
|
||||
|
||||
if (pokemon.value && selectedTab === habitatTabValue(pokemon.value.environment.id)) {
|
||||
return rows
|
||||
.filter((item) => habitatTabValue(item.environment.id) === selectedTab || item.environment.isOpposite)
|
||||
.slice(0, relatedPokemonLimit);
|
||||
return rows.filter((item) => habitatTabValue(item.environment.id) === selectedTab || item.environment.isOpposite);
|
||||
}
|
||||
|
||||
return rows.filter((item) => habitatTabValue(item.environment.id) === selectedTab).slice(0, relatedPokemonLimit);
|
||||
return rows.filter((item) => habitatTabValue(item.environment.id) === selectedTab);
|
||||
});
|
||||
const typeSlotClass = computed(() => ({
|
||||
'pokemon-type-slots--single': (pokemon.value?.types.length ?? 0) === 1
|
||||
|
||||
Reference in New Issue
Block a user