Før simulatorer
This commit is contained in:
parent
a508db6071
commit
9af374adda
5 changed files with 249 additions and 188 deletions
419
backend/main.py
419
backend/main.py
|
|
@ -816,6 +816,7 @@ FACILITY_VIEW_SEARCH_FIELDS = {
|
|||
'course_statuses', 'weather_forecast',
|
||||
}
|
||||
FACILITY_VIEW_PLACE_FIELDS = FACILITY_VIEW_SEARCH_FIELDS | {
|
||||
'has_golfpakker',
|
||||
'greenfee', 'standard_medlemskap', 'total_hole_count', 'hole_par_counts',
|
||||
'shortest_hole_meters', 'longest_hole_meters',
|
||||
}
|
||||
|
|
@ -1063,6 +1064,29 @@ async def resolve_cooperating_club_slugs(
|
|||
return resolved_slugs
|
||||
|
||||
|
||||
async def ensure_public_query_indexes(conn) -> None:
|
||||
await conn.execute("""
|
||||
CREATE INDEX IF NOT EXISTS facilities_is_published_name_idx
|
||||
ON facilities (is_published, name)
|
||||
""")
|
||||
await conn.execute("""
|
||||
CREATE INDEX IF NOT EXISTS courses_facility_id_idx
|
||||
ON courses (facility_id)
|
||||
""")
|
||||
await conn.execute("""
|
||||
CREATE INDEX IF NOT EXISTS courses_facility_id_main_idx
|
||||
ON courses (facility_id, is_main_course DESC, id ASC)
|
||||
""")
|
||||
await conn.execute("""
|
||||
CREATE INDEX IF NOT EXISTS holes_course_id_idx
|
||||
ON holes (course_id)
|
||||
""")
|
||||
await conn.execute("""
|
||||
CREATE INDEX IF NOT EXISTS facility_weather_forecast_facility_day_idx
|
||||
ON facility_weather_forecast (facility_id, day_offset)
|
||||
""")
|
||||
|
||||
|
||||
async def get_table_columns(conn, table_name: str, schema_name: str = "public") -> set[str]:
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
|
|
@ -1779,7 +1803,7 @@ async def get_published_facility_by_slug(conn, slug: str):
|
|||
SELECT id, name, slug
|
||||
FROM facilities
|
||||
WHERE slug = $1
|
||||
AND COALESCE(is_published, TRUE) = TRUE
|
||||
AND is_published IS DISTINCT FROM FALSE
|
||||
LIMIT 1
|
||||
""",
|
||||
slug,
|
||||
|
|
@ -2218,6 +2242,7 @@ async def lifespan(app: FastAPI):
|
|||
await ensure_scrape_jobs_table(conn)
|
||||
await ensure_course_status_history_table(conn)
|
||||
await ensure_weather_forecast_table(conn)
|
||||
await ensure_public_query_indexes(conn)
|
||||
app.state.weather_sync_stop_event = asyncio.Event()
|
||||
app.state.weather_sync_task = asyncio.create_task(
|
||||
weather_sync_loop(app.state.pool, app.state.weather_sync_stop_event)
|
||||
|
|
@ -2897,142 +2922,158 @@ async def upsert_facility_rating(request: Request, payload: FacilityRatingUpsert
|
|||
|
||||
def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | None]:
|
||||
normalized_view = (view or "").strip().lower()
|
||||
|
||||
course_statuses_sql = """
|
||||
(
|
||||
SELECT jsonb_agg(cs) FROM (
|
||||
SELECT id, name, status FROM courses
|
||||
WHERE facility_id = f.id AND status != 'finnes_ingen_bane_to'
|
||||
ORDER BY is_main_course DESC, id ASC
|
||||
) cs
|
||||
) as course_statuses
|
||||
published_facilities_cte = """
|
||||
published_facilities AS (
|
||||
SELECT *
|
||||
FROM facilities
|
||||
WHERE is_published IS DISTINCT FROM FALSE
|
||||
)
|
||||
"""
|
||||
weather_compact_sql = """
|
||||
(
|
||||
SELECT jsonb_agg(w_data ORDER BY w_data.day_offset ASC) FROM (
|
||||
SELECT
|
||||
day_offset,
|
||||
dry_daylight
|
||||
FROM facility_weather_forecast
|
||||
WHERE facility_id = f.id
|
||||
ORDER BY day_offset ASC
|
||||
) w_data
|
||||
) as weather_forecast
|
||||
course_statuses_cte = """
|
||||
course_statuses AS (
|
||||
SELECT
|
||||
c.facility_id,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', c.id,
|
||||
'name', c.name,
|
||||
'status', c.status
|
||||
)
|
||||
ORDER BY c.is_main_course DESC, c.id ASC
|
||||
) AS course_statuses
|
||||
FROM courses c
|
||||
JOIN published_facilities pf ON pf.id = c.facility_id
|
||||
WHERE c.status != 'finnes_ingen_bane_to'
|
||||
GROUP BY c.facility_id
|
||||
)
|
||||
"""
|
||||
weather_full_sql = """
|
||||
(
|
||||
SELECT jsonb_agg(w_data ORDER BY w_data.day_offset ASC) FROM (
|
||||
SELECT
|
||||
forecast_date,
|
||||
day_offset,
|
||||
dry_all_day,
|
||||
dry_daylight,
|
||||
precip_mm,
|
||||
precip_probability_max,
|
||||
daylight_precip_mm,
|
||||
daylight_precip_probability_max,
|
||||
confidence,
|
||||
source_updated_at,
|
||||
source_expires_at,
|
||||
calculated_at
|
||||
FROM facility_weather_forecast
|
||||
WHERE facility_id = f.id
|
||||
ORDER BY day_offset ASC
|
||||
) w_data
|
||||
) as weather_forecast
|
||||
weather_compact_cte = """
|
||||
weather_compact AS (
|
||||
SELECT
|
||||
facility_id,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'day_offset', day_offset,
|
||||
'dry_daylight', dry_daylight
|
||||
)
|
||||
ORDER BY day_offset ASC
|
||||
) AS weather_forecast
|
||||
FROM facility_weather_forecast
|
||||
WHERE facility_id IN (SELECT id FROM published_facilities)
|
||||
GROUP BY facility_id
|
||||
)
|
||||
"""
|
||||
weather_full_cte = """
|
||||
weather_full AS (
|
||||
SELECT
|
||||
facility_id,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'forecast_date', forecast_date,
|
||||
'day_offset', day_offset,
|
||||
'dry_all_day', dry_all_day,
|
||||
'dry_daylight', dry_daylight,
|
||||
'precip_mm', precip_mm,
|
||||
'precip_probability_max', precip_probability_max,
|
||||
'daylight_precip_mm', daylight_precip_mm,
|
||||
'daylight_precip_probability_max', daylight_precip_probability_max,
|
||||
'confidence', confidence,
|
||||
'source_updated_at', source_updated_at,
|
||||
'source_expires_at', source_expires_at,
|
||||
'calculated_at', calculated_at
|
||||
)
|
||||
ORDER BY day_offset ASC
|
||||
) AS weather_forecast
|
||||
FROM facility_weather_forecast
|
||||
WHERE facility_id IN (SELECT id FROM published_facilities)
|
||||
GROUP BY facility_id
|
||||
)
|
||||
"""
|
||||
hole_counts_cte = """
|
||||
hole_counts AS (
|
||||
SELECT
|
||||
c.facility_id,
|
||||
COUNT(h.id) AS total_hole_count,
|
||||
jsonb_build_object(
|
||||
'3', COUNT(*) FILTER (WHERE h.par = 3),
|
||||
'4', COUNT(*) FILTER (WHERE h.par = 4),
|
||||
'5', COUNT(*) FILTER (WHERE h.par = 5),
|
||||
'6', COUNT(*) FILTER (WHERE h.par = 6)
|
||||
) AS hole_par_counts
|
||||
FROM courses c
|
||||
JOIN holes h ON h.course_id = c.id
|
||||
JOIN published_facilities pf ON pf.id = c.facility_id
|
||||
GROUP BY c.facility_id
|
||||
)
|
||||
"""
|
||||
hole_lengths_cte = """
|
||||
hole_lengths AS (
|
||||
SELECT
|
||||
c.facility_id,
|
||||
MIN((length_value.value)::int) AS shortest_hole_meters,
|
||||
MAX((length_value.value)::int) AS longest_hole_meters
|
||||
FROM courses c
|
||||
JOIN holes h ON h.course_id = c.id
|
||||
JOIN published_facilities pf ON pf.id = c.facility_id
|
||||
CROSS JOIN LATERAL jsonb_each_text(COALESCE(h.lengths, '{}'::jsonb)) AS length_value(key, value)
|
||||
WHERE length_value.key IN ('kortest', 'kort', 'mellomkort', 'mellomlang', 'lang', 'lengst')
|
||||
AND length_value.value ~ '^[0-9]+$'
|
||||
AND (length_value.value)::int BETWEEN 30 AND 900
|
||||
GROUP BY c.facility_id
|
||||
)
|
||||
"""
|
||||
has_golfpakker_sql = """
|
||||
CASE
|
||||
WHEN jsonb_typeof(f.golfpakker) = 'array' AND jsonb_array_length(f.golfpakker) > 0 THEN TRUE
|
||||
WHEN NULLIF(BTRIM(COALESCE(f.golfpakker_url, '')), '') IS NOT NULL THEN TRUE
|
||||
WHEN jsonb_typeof(pf.golfpakker) = 'array' AND jsonb_array_length(pf.golfpakker) > 0 THEN TRUE
|
||||
WHEN NULLIF(BTRIM(COALESCE(pf.golfpakker_url, '')), '') IS NOT NULL THEN TRUE
|
||||
ELSE FALSE
|
||||
END as has_golfpakker
|
||||
"""
|
||||
total_hole_count_sql = """
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM holes h
|
||||
JOIN courses c ON c.id = h.course_id
|
||||
WHERE c.facility_id = f.id
|
||||
) as total_hole_count
|
||||
"""
|
||||
hole_par_counts_sql = """
|
||||
(
|
||||
SELECT jsonb_build_object(
|
||||
'3', COUNT(*) FILTER (WHERE h.par = 3),
|
||||
'4', COUNT(*) FILTER (WHERE h.par = 4),
|
||||
'5', COUNT(*) FILTER (WHERE h.par = 5),
|
||||
'6', COUNT(*) FILTER (WHERE h.par = 6)
|
||||
)
|
||||
FROM holes h
|
||||
JOIN courses c ON c.id = h.course_id
|
||||
WHERE c.facility_id = f.id
|
||||
) as hole_par_counts
|
||||
"""
|
||||
shortest_hole_sql = """
|
||||
(
|
||||
SELECT MIN((length_value.value)::int)
|
||||
FROM holes h
|
||||
JOIN courses c ON c.id = h.course_id
|
||||
CROSS JOIN LATERAL jsonb_each_text(COALESCE(h.lengths, '{}'::jsonb)) AS length_value(key, value)
|
||||
WHERE c.facility_id = f.id
|
||||
AND length_value.key IN ('kortest', 'kort', 'mellomkort', 'mellomlang', 'lang', 'lengst')
|
||||
AND length_value.value ~ '^[0-9]+$'
|
||||
AND (length_value.value)::int BETWEEN 30 AND 900
|
||||
) as shortest_hole_meters
|
||||
"""
|
||||
longest_hole_sql = """
|
||||
(
|
||||
SELECT MAX((length_value.value)::int)
|
||||
FROM holes h
|
||||
JOIN courses c ON c.id = h.course_id
|
||||
CROSS JOIN LATERAL jsonb_each_text(COALESCE(h.lengths, '{}'::jsonb)) AS length_value(key, value)
|
||||
WHERE c.facility_id = f.id
|
||||
AND length_value.key IN ('kortest', 'kort', 'mellomkort', 'mellomlang', 'lang', 'lengst')
|
||||
AND length_value.value ~ '^[0-9]+$'
|
||||
AND (length_value.value)::int BETWEEN 30 AND 900
|
||||
) as longest_hole_meters
|
||||
END AS has_golfpakker
|
||||
"""
|
||||
|
||||
if normalized_view in {"search", "home"}:
|
||||
return (
|
||||
f"""
|
||||
WITH
|
||||
{published_facilities_cte},
|
||||
{course_statuses_cte},
|
||||
{weather_compact_cte}
|
||||
SELECT
|
||||
f.id,
|
||||
f.slug,
|
||||
f.name,
|
||||
f.architect,
|
||||
f.description,
|
||||
f.city,
|
||||
f.county,
|
||||
f.banetype,
|
||||
f.image_url,
|
||||
f.phone,
|
||||
f.website_url,
|
||||
f.golfbox_booking_url,
|
||||
f.golfbox_tournament_url,
|
||||
f.weather_url,
|
||||
f.lat,
|
||||
f.lng,
|
||||
f.golfamore,
|
||||
f.golfamore_url,
|
||||
f.nsg_url,
|
||||
pf.id,
|
||||
pf.slug,
|
||||
pf.name,
|
||||
pf.architect,
|
||||
pf.description,
|
||||
pf.city,
|
||||
pf.county,
|
||||
pf.banetype,
|
||||
pf.image_url,
|
||||
pf.phone,
|
||||
pf.website_url,
|
||||
pf.golfbox_booking_url,
|
||||
pf.golfbox_tournament_url,
|
||||
pf.weather_url,
|
||||
pf.lat,
|
||||
pf.lng,
|
||||
pf.golfamore,
|
||||
pf.golfamore_url,
|
||||
pf.nsg_url,
|
||||
{has_golfpakker_sql},
|
||||
f.vtg_pris,
|
||||
f.vtg_lenke,
|
||||
f.vtg_beskrivelse,
|
||||
f.amenities,
|
||||
f.golfamore_data,
|
||||
f.nsg_data,
|
||||
f.vtg_datoer,
|
||||
f.footnote,
|
||||
f.footnote_updated_at,
|
||||
f.status_updated_at,
|
||||
{course_statuses_sql},
|
||||
{weather_compact_sql}
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
ORDER BY f.name ASC
|
||||
pf.vtg_pris,
|
||||
pf.vtg_lenke,
|
||||
pf.vtg_beskrivelse,
|
||||
pf.amenities,
|
||||
pf.golfamore_data,
|
||||
pf.nsg_data,
|
||||
pf.vtg_datoer,
|
||||
pf.footnote,
|
||||
pf.footnote_updated_at,
|
||||
pf.status_updated_at,
|
||||
COALESCE(cs.course_statuses, '[]'::jsonb) AS course_statuses,
|
||||
COALESCE(wc.weather_forecast, '[]'::jsonb) AS weather_forecast
|
||||
FROM published_facilities pf
|
||||
LEFT JOIN course_statuses cs ON cs.facility_id = pf.id
|
||||
LEFT JOIN weather_compact wc ON wc.facility_id = pf.id
|
||||
ORDER BY pf.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_SEARCH_FIELDS,
|
||||
)
|
||||
|
|
@ -3040,48 +3081,57 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
if normalized_view == "place":
|
||||
return (
|
||||
f"""
|
||||
WITH
|
||||
{published_facilities_cte},
|
||||
{course_statuses_cte},
|
||||
{weather_compact_cte},
|
||||
{hole_counts_cte},
|
||||
{hole_lengths_cte}
|
||||
SELECT
|
||||
f.id,
|
||||
f.slug,
|
||||
f.name,
|
||||
f.architect,
|
||||
f.description,
|
||||
f.city,
|
||||
f.county,
|
||||
f.banetype,
|
||||
f.image_url,
|
||||
f.phone,
|
||||
f.website_url,
|
||||
f.golfbox_booking_url,
|
||||
f.golfbox_tournament_url,
|
||||
f.weather_url,
|
||||
f.lat,
|
||||
f.lng,
|
||||
f.golfamore,
|
||||
f.golfamore_url,
|
||||
f.nsg_url,
|
||||
pf.id,
|
||||
pf.slug,
|
||||
pf.name,
|
||||
pf.architect,
|
||||
pf.description,
|
||||
pf.city,
|
||||
pf.county,
|
||||
pf.banetype,
|
||||
pf.image_url,
|
||||
pf.phone,
|
||||
pf.website_url,
|
||||
pf.golfbox_booking_url,
|
||||
pf.golfbox_tournament_url,
|
||||
pf.weather_url,
|
||||
pf.lat,
|
||||
pf.lng,
|
||||
pf.golfamore,
|
||||
pf.golfamore_url,
|
||||
pf.nsg_url,
|
||||
{has_golfpakker_sql},
|
||||
f.greenfee,
|
||||
f.standard_medlemskap,
|
||||
f.vtg_pris,
|
||||
f.vtg_lenke,
|
||||
f.vtg_beskrivelse,
|
||||
f.amenities,
|
||||
f.golfamore_data,
|
||||
f.nsg_data,
|
||||
f.vtg_datoer,
|
||||
f.footnote,
|
||||
f.footnote_updated_at,
|
||||
f.status_updated_at,
|
||||
{course_statuses_sql},
|
||||
{total_hole_count_sql},
|
||||
{hole_par_counts_sql},
|
||||
{shortest_hole_sql},
|
||||
{longest_hole_sql},
|
||||
{weather_compact_sql}
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
ORDER BY f.name ASC
|
||||
pf.greenfee,
|
||||
pf.standard_medlemskap,
|
||||
pf.vtg_pris,
|
||||
pf.vtg_lenke,
|
||||
pf.vtg_beskrivelse,
|
||||
pf.amenities,
|
||||
pf.golfamore_data,
|
||||
pf.nsg_data,
|
||||
pf.vtg_datoer,
|
||||
pf.footnote,
|
||||
pf.footnote_updated_at,
|
||||
pf.status_updated_at,
|
||||
COALESCE(cs.course_statuses, '[]'::jsonb) AS course_statuses,
|
||||
COALESCE(hc.total_hole_count, 0) AS total_hole_count,
|
||||
COALESCE(hc.hole_par_counts, jsonb_build_object('3', 0, '4', 0, '5', 0, '6', 0)) AS hole_par_counts,
|
||||
hl.shortest_hole_meters,
|
||||
hl.longest_hole_meters,
|
||||
COALESCE(wc.weather_forecast, '[]'::jsonb) AS weather_forecast
|
||||
FROM published_facilities pf
|
||||
LEFT JOIN course_statuses cs ON cs.facility_id = pf.id
|
||||
LEFT JOIN weather_compact wc ON wc.facility_id = pf.id
|
||||
LEFT JOIN hole_counts hc ON hc.facility_id = pf.id
|
||||
LEFT JOIN hole_lengths hl ON hl.facility_id = pf.id
|
||||
ORDER BY pf.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_PLACE_FIELDS,
|
||||
)
|
||||
|
|
@ -3103,7 +3153,7 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
f.navn_rimeligste_alternativ,
|
||||
f.rimeligste_alternativ
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
WHERE f.is_published IS DISTINCT FROM FALSE
|
||||
ORDER BY f.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_MEMBERSHIP_FIELDS,
|
||||
|
|
@ -3127,7 +3177,7 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
f.vtg_datoer,
|
||||
f.vtg_updated_at
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
WHERE f.is_published IS DISTINCT FROM FALSE
|
||||
ORDER BY f.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_VTG_FIELDS,
|
||||
|
|
@ -3144,7 +3194,7 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
f.county,
|
||||
f.ngf_number
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
WHERE f.is_published IS DISTINCT FROM FALSE
|
||||
ORDER BY f.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_CLUBNUMBERS_FIELDS,
|
||||
|
|
@ -3158,7 +3208,7 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
f.status_updated_at,
|
||||
f.vtg_updated_at
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
WHERE f.is_published IS DISTINCT FROM FALSE
|
||||
ORDER BY f.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_SITEMAP_FIELDS,
|
||||
|
|
@ -3171,7 +3221,7 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
f.slug,
|
||||
f.name
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
WHERE f.is_published IS DISTINCT FROM FALSE
|
||||
ORDER BY f.name ASC
|
||||
""",
|
||||
FACILITY_VIEW_ALIASES_FIELDS,
|
||||
|
|
@ -3179,17 +3229,26 @@ def build_public_facilities_query(view: str | None) -> tuple[str, set[str] | Non
|
|||
|
||||
return (
|
||||
f"""
|
||||
WITH
|
||||
{published_facilities_cte},
|
||||
{course_statuses_cte},
|
||||
{weather_full_cte},
|
||||
{hole_counts_cte},
|
||||
{hole_lengths_cte}
|
||||
SELECT
|
||||
f.*,
|
||||
{course_statuses_sql},
|
||||
{total_hole_count_sql},
|
||||
{hole_par_counts_sql},
|
||||
{shortest_hole_sql},
|
||||
{longest_hole_sql},
|
||||
{weather_full_sql}
|
||||
FROM facilities f
|
||||
WHERE COALESCE(f.is_published, TRUE) = TRUE
|
||||
ORDER BY f.name ASC
|
||||
pf.*,
|
||||
COALESCE(cs.course_statuses, '[]'::jsonb) AS course_statuses,
|
||||
COALESCE(hc.total_hole_count, 0) AS total_hole_count,
|
||||
COALESCE(hc.hole_par_counts, jsonb_build_object('3', 0, '4', 0, '5', 0, '6', 0)) AS hole_par_counts,
|
||||
hl.shortest_hole_meters,
|
||||
hl.longest_hole_meters,
|
||||
COALESCE(wf.weather_forecast, '[]'::jsonb) AS weather_forecast
|
||||
FROM published_facilities pf
|
||||
LEFT JOIN course_statuses cs ON cs.facility_id = pf.id
|
||||
LEFT JOIN hole_counts hc ON hc.facility_id = pf.id
|
||||
LEFT JOIN hole_lengths hl ON hl.facility_id = pf.id
|
||||
LEFT JOIN weather_full wf ON wf.facility_id = pf.id
|
||||
ORDER BY pf.name ASC
|
||||
""",
|
||||
None,
|
||||
)
|
||||
|
|
@ -3266,7 +3325,7 @@ async def get_facility(slug: str, response: Response):
|
|||
) as weather_forecast
|
||||
FROM facilities f
|
||||
WHERE f.slug = $1
|
||||
AND COALESCE(f.is_published, TRUE) = TRUE
|
||||
AND f.is_published IS DISTINCT FROM FALSE
|
||||
""", slug)
|
||||
|
||||
if not row:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { API_URL } from "@/config/constants";
|
||||
import { getAvailablePlaceConfigs, slugify } from "@/app/facilityData";
|
||||
import { cache } from "react";
|
||||
|
||||
type FacilityAliasSource = {
|
||||
slug?: string | null;
|
||||
|
|
@ -114,9 +115,9 @@ function buildFacilityAliasMap(facilities: FacilityAliasSource[]) {
|
|||
return Object.fromEntries(aliases);
|
||||
}
|
||||
|
||||
async function getFacilityAliasMap() {
|
||||
const getFacilityAliasMap = cache(async () => {
|
||||
const response = await fetch(`${API_URL}/facilities?view=aliases`, {
|
||||
next: { revalidate: 3600 },
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
@ -126,7 +127,7 @@ async function getFacilityAliasMap() {
|
|||
const data = await response.json();
|
||||
const facilities = Array.isArray(data) ? (data as FacilityAliasSource[]) : [];
|
||||
return buildFacilityAliasMap(facilities);
|
||||
}
|
||||
});
|
||||
|
||||
export async function resolveFacilityAlias(alias: string) {
|
||||
const normalizedAlias = slugify(alias);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Metadata } from "next";
|
||||
import { cookies } from "next/headers";
|
||||
import { notFound, permanentRedirect } from "next/navigation";
|
||||
import { cache } from "react";
|
||||
import { API_URL } from "@/config/constants";
|
||||
import { resolveFacilityAlias } from "@/app/facilityAliases";
|
||||
import type { FacilityRecord } from "@/app/facilityData";
|
||||
|
|
@ -23,7 +24,7 @@ type FacilityPageData = FacilityRecord & {
|
|||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
async function getFacility(slug: string) {
|
||||
const getFacility = cache(async (slug: string) => {
|
||||
const res = await fetch(`${API_URL}/facilities/${slug}`, { cache: "no-store" });
|
||||
if (!res.ok) {
|
||||
return null;
|
||||
|
|
@ -35,7 +36,7 @@ async function getFacility(slug: string) {
|
|||
}
|
||||
|
||||
return facility as FacilityPageData;
|
||||
}
|
||||
});
|
||||
|
||||
async function getCanonicalSlug(slug: string) {
|
||||
const canonicalSlug = await resolveFacilityAlias(slug);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|||
|
||||
export async function fetchPublicFacilities<T>(
|
||||
view: string,
|
||||
revalidateSeconds: number,
|
||||
_revalidateSeconds: number,
|
||||
{
|
||||
allowEmpty = false,
|
||||
attempts = 3,
|
||||
|
|
@ -18,7 +18,7 @@ export async function fetchPublicFacilities<T>(
|
|||
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/facilities?view=${view}`, {
|
||||
next: { revalidate: revalidateSeconds },
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ export default async function PlacePage({ params }: { params: Promise<{ slug: st
|
|||
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/place-pages/${slug}`, {
|
||||
next: { revalidate },
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue