diff --git a/app.config.ts b/app.config.ts index 80fea5479..01d094f45 100644 --- a/app.config.ts +++ b/app.config.ts @@ -54,12 +54,13 @@ const vinxiConfig = defineConfig({ maxAge: 60 * 60 * 24 * 360, }, ], - plugins: ['./src/server/admin/nitroPlugin.ts'], + plugins: ['./src/server/admin/index.ts'], commonJS: { include: ['dom-helpers', 'maplibre-gl'], }, experimental: { legacyExternals: true, + asyncContext: true, }, }, tsr: { diff --git a/cypress/e2e/routing/routing.test.ts b/cypress/e2e/routing/routing.test.ts index 7dfe2d696..bedbd827a 100644 --- a/cypress/e2e/routing/routing.test.ts +++ b/cypress/e2e/routing/routing.test.ts @@ -101,6 +101,14 @@ describe('Routing', () => { ); }); + it('with start, destination & time = 0', () => { + cy.visit('/routing/8000105/8002549/0'); + searchInput('routingStartSearch', 'Frankfurt(Main)Hbf'); + searchInput('routingDestinationSearch', 'Hamburg Hbf'); + searchInput('addVia', ''); + cy.findByTestId('routingDatePicker').should('contain.value', 'Jetzt'); + }); + it('with start, destination, time & 1 via', () => { cy.visit('/routing/8000105/8002549/2020-11-17T10:00:15.589Z/8000244|'); searchInput('routingStartSearch', 'Frankfurt(Main)Hbf'); diff --git a/cypress/fixtures/details/ICE720.json b/cypress/fixtures/details/ICE720.json index 77816d300..33107f706 100644 --- a/cypress/fixtures/details/ICE720.json +++ b/cypress/fixtures/details/ICE720.json @@ -400,9 +400,7 @@ "admin": "80", "line": "41", "transportType": "HIGH_SPEED_TRAIN", - "operator": { - "name": "DB" - } + "operator": "DB" }, "type": "JNY", "currentStop": { diff --git a/cypress/fixtures/details/S6.json b/cypress/fixtures/details/S6.json index 1507ad86c..7e9031529 100644 --- a/cypress/fixtures/details/S6.json +++ b/cypress/fixtures/details/S6.json @@ -16,10 +16,7 @@ "admin": "800337", "number": "30665", "type": "S", - "operator": { - "name": "DB Regio AG NRW", - "icoX": 2 - } + "operator": "DB Regio AG NRW" }, "segmentStart": { "name": "Köln-Nippes", diff --git a/scripts/clearCorruptCache.ts b/scripts/clearCorruptCache.ts deleted file mode 100644 index 7fff0be80..000000000 --- a/scripts/clearCorruptCache.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Cache, CacheDatabase } from '@/server/cache'; - -const cachesThatMightBeBroken = [ - // CacheDatabase.StopPlaceSearch, - // CacheDatabase.StopPlaceSalesSearch, - // CacheDatabase.StopPlaceByEva, - // CacheDatabase.StopPlaceByRil, - CacheDatabase.JourneyFind, - CacheDatabase.JourneyFindV2, -].map((cacheNumber) => { - return new Cache(cacheNumber); -}); - -async function doStuff() { - for (const cache of cachesThatMightBeBroken) { - const allEntries = await cache.getAll(); - const relevantEntries = allEntries.filter( - ([, value]) => !value || (Array.isArray(value) && !value.length), - ); - // biome-ignore lint/suspicious/noConsoleLog: script - console.log(`clearing ${relevantEntries.length}`); - for (const [key] of relevantEntries) { - await cache.delete(key); - } - } -} - -doStuff().then(() => { - process.exit(0); -}); diff --git a/src/bahnde/journeyDetails/journeyDetails.ts b/src/bahnde/journeyDetails/journeyDetails.ts index 3beb29d60..aa9ecec34 100644 --- a/src/bahnde/journeyDetails/journeyDetails.ts +++ b/src/bahnde/journeyDetails/journeyDetails.ts @@ -1,9 +1,10 @@ import { mapFahrt } from '@/bahnde/journeyDetails/parseJourneyDetails'; import { addRandomBrowserUseragent } from '@/bahnde/randomUseragent'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { logger } from '@/server/logger'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; + import axios from 'axios'; import { v4 } from 'uuid'; @@ -21,13 +22,11 @@ journeyDetailsAxios.interceptors.request.use((req) => { }); axiosUpstreamInterceptor(journeyDetailsAxios, 'bahn.de-journeyDetails'); -const quickJourneyDetailsCache = new Cache( - CacheDatabase.BahnDEJourneyDetails, -); +const quickJourneyDetailsCache = getCache(CacheDatabase.BahnDEJourneyDetails); export const bahnJourneyDetails = async ( jid: string, -): Promise => { +): Promise => { try { if (await quickJourneyDetailsCache.exists(jid)) { return quickJourneyDetailsCache.get(jid); diff --git a/src/bahnde/journeyDetails/parseJourneyDetails.ts b/src/bahnde/journeyDetails/parseJourneyDetails.ts index 716c66956..e92109485 100644 --- a/src/bahnde/journeyDetails/parseJourneyDetails.ts +++ b/src/bahnde/journeyDetails/parseJourneyDetails.ts @@ -1,10 +1,10 @@ import { mapHalt } from '@/bahnde/parsing'; import type { BahnDEFahrt } from '@/bahnde/types'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; export async function mapFahrt( input?: BahnDEFahrt, -): Promise { +): Promise { if (!input) { return undefined; } diff --git a/src/bahnde/occupancy.ts b/src/bahnde/occupancy.ts index 53bfac2c2..4aa5e6e21 100644 --- a/src/bahnde/occupancy.ts +++ b/src/bahnde/occupancy.ts @@ -1,6 +1,6 @@ import { routing } from '@/bahnde/routing/routing'; import { searchStopPlace } from '@/server/StopPlace/search'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { timezone } from '@/timezone'; import type { RouteAuslastung, @@ -59,9 +59,7 @@ async function getRelevantTrip( return relevantTrip; } -const stopOccupancyCache = new Cache( - CacheDatabase.HafasStopOccupancy, -); +const stopOccupancyCache = getCache(CacheDatabase.HafasStopOccupancy); const createBaseCacheKey = ( trainNumber: string, diff --git a/src/bahnde/parsing.ts b/src/bahnde/parsing.ts index 86d6aa029..3952a2e95 100644 --- a/src/bahnde/parsing.ts +++ b/src/bahnde/parsing.ts @@ -5,12 +5,12 @@ import type { BahnDERoutingAbschnitt, } from '@/bahnde/types'; import { getStopPlaceByEva } from '@/server/StopPlace/search'; -import type { CommonStopInfo } from '@/types/HAFAS'; import { AuslastungsValue, type RouteAuslastung, type RouteStop, } from '@/types/routing'; +import type { CommonStopInfo } from '@/types/stopPlace'; import { tz } from '@date-fns/tz'; import { differenceInMinutes, parseISO } from 'date-fns'; diff --git a/src/bahnde/routing/parseRouting.ts b/src/bahnde/routing/parseRouting.ts index 912bddf3e..08ed817b0 100644 --- a/src/bahnde/routing/parseRouting.ts +++ b/src/bahnde/routing/parseRouting.ts @@ -13,12 +13,13 @@ import type { } from '@/bahnde/types'; import { TransportType } from '@/external/types'; import { getStopPlaceByEva } from '@/server/StopPlace/search'; -import type { HafasStation, ParsedProduct } from '@/types/HAFAS'; +import type { CommonProductInfo } from '@/types/journey'; import type { RouteJourneySegment, RouteJourneySegmentTrain, RouteJourneySegmentWalk, RoutingResult, + RoutingStopPlace, SingleRoute, } from '@/types/routing'; import { differenceInMilliseconds, differenceInMinutes } from 'date-fns'; @@ -26,7 +27,7 @@ import { differenceInMilliseconds, differenceInMinutes } from 'date-fns'; const mapWalkSegmentStartDestination = async ( input: BahnDERoutingAbschnitt, prefix: 'ankunfts' | 'abfahrts', -): Promise => { +): Promise => { let evaNumber = input[`${prefix}OrtExtId`]; if (evaNumber.startsWith('0')) { evaNumber = evaNumber.substring(1); @@ -36,25 +37,17 @@ const mapWalkSegmentStartDestination = async ( return { name: input[`${prefix}Ort`], evaNumber, - // TODO: make this optional - coordinates: { - lat: 0, - lng: 0, - }, }; } return { - coordinates: { - lat: risStopPlace.position?.latitude || 0, - lng: risStopPlace.position?.longitude || 0, - }, + position: risStopPlace.position, evaNumber, name: risStopPlace.name, ril100: risStopPlace.ril100, }; }; -const mapProduct = (input: BahnDEVerkehrsmittel): ParsedProduct => { +const mapProduct = (input: BahnDEVerkehrsmittel): CommonProductInfo => { return { name: input.langText || input.mittelText || input.name, line: input.linienNummer, @@ -62,9 +55,7 @@ const mapProduct = (input: BahnDEVerkehrsmittel): ParsedProduct => { transportType: TransportType.Unknown, number: input.nummer === input.linienNummer ? '0' : input.nummer, type: input.kurzText || input.kategorie, - operator: { - name: input.zugattribute?.filter((a) => a.key === 'BEF').join(', '), - }, + operator: input.zugattribute?.filter((a) => a.key === 'BEF').join(', '), }; }; diff --git a/src/client/Abfahrten/Components/ExtraMenu.tsx b/src/client/Abfahrten/Components/ExtraMenu.tsx index c0c3e8aae..c71d3abd9 100644 --- a/src/client/Abfahrten/Components/ExtraMenu.tsx +++ b/src/client/Abfahrten/Components/ExtraMenu.tsx @@ -17,11 +17,7 @@ import { useCallback, useState } from 'react'; import type { FC, SyntheticEvent } from 'react'; import { FilterModal } from './FilterModal'; -interface Props { - favKey: 'regionalFavs' | 'favs'; -} - -export const ExtraMenu: FC = ({ favKey }) => { +export const ExtraMenu: FC = () => { const setFilterOpen = useAbfahrtenFilterOpen(); const currentStopPlace = useCurrentAbfahrtenStopPlace(); const { data: lageplan } = trpc.stopPlace.lageplan.useQuery( @@ -31,9 +27,9 @@ export const ExtraMenu: FC = ({ favKey }) => { staleTime: Number.POSITIVE_INFINITY, }, ); - const favs = useFavs(favKey); - const fav = useFavAction(favKey); - const unfav = useUnfavAction(favKey); + const favs = useFavs(); + const fav = useFavAction(); + const unfav = useUnfavAction(); const isFaved = Boolean(currentStopPlace && favs[currentStopPlace.evaNumber]); const [anchor, setAnchor] = useState(); const toggleFav = useCallback(() => { diff --git a/src/client/Abfahrten/Components/FavEntry.tsx b/src/client/Abfahrten/Components/FavEntry.tsx index 88d0b9f6d..f85d3140b 100644 --- a/src/client/Abfahrten/Components/FavEntry.tsx +++ b/src/client/Abfahrten/Components/FavEntry.tsx @@ -38,8 +38,8 @@ const UnclickablePaper = styled(BasePaper)` interface Props { fav: MinimalStopPlace; - favKey?: 'regionalFavs' | 'favs'; 'data-testid'?: string; + noDelete?: boolean; } interface FavEntryDisplayProps { @@ -75,14 +75,14 @@ export const FavEntryDisplay: FC = ({ export const FavEntry: FC = ({ fav, 'data-testid': testid = 'favEntry', - favKey, + noDelete, }) => { - const unfav = useUnfavAction(favKey); + const unfav = useUnfavAction(); const deleteFav = useCallback( (e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); - unfav?.(fav); + unfav(fav); }, [fav, unfav], ); @@ -98,7 +98,7 @@ export const FavEntry: FC = ({ > ); diff --git a/src/client/Abfahrten/Components/FavList.tsx b/src/client/Abfahrten/Components/FavList.tsx index fa9bdf411..4434b669b 100644 --- a/src/client/Abfahrten/Components/FavList.tsx +++ b/src/client/Abfahrten/Components/FavList.tsx @@ -10,19 +10,18 @@ import { FavEntry, FavEntryDisplay } from './FavEntry'; interface Props { children?: ReactNode; - favKey: 'regionalFavs' | 'favs'; mostUsed?: boolean; } -export const FavList: FC = ({ children, favKey, mostUsed }) => { - const favs = useFavs(favKey); +export const FavList: FC = ({ children, mostUsed }) => { + const favs = useFavs(); const sortedFavs = useMemo(() => { const values: MinimalStopPlace[] = Object.values(favs); return values .sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1)) - .map((fav) => ); - }, [favs, favKey]); + .map((fav) => ); + }, [favs]); const { updateTitle, updateDescription, updateKeywords } = useHeaderTagsActions(); diff --git a/src/client/Abfahrten/Components/Header.tsx b/src/client/Abfahrten/Components/Header.tsx index 6a569c6e2..5d0f0d13c 100644 --- a/src/client/Abfahrten/Components/Header.tsx +++ b/src/client/Abfahrten/Components/Header.tsx @@ -71,7 +71,7 @@ export const Header: FC = ({ regional = false }: Props) => { )} - + ); }; diff --git a/src/client/Abfahrten/Components/MostUsed.tsx b/src/client/Abfahrten/Components/MostUsed.tsx index bcb0eefa3..fbfaaa5b9 100644 --- a/src/client/Abfahrten/Components/MostUsed.tsx +++ b/src/client/Abfahrten/Components/MostUsed.tsx @@ -22,7 +22,12 @@ export const MostUsed: FC = () => { return ( <> {mostUsed.map((m) => ( - + ))} ); diff --git a/src/client/Abfahrten/hooks/useFavs.ts b/src/client/Abfahrten/hooks/useFavs.ts index 8b648902f..5198196ae 100644 --- a/src/client/Abfahrten/hooks/useFavs.ts +++ b/src/client/Abfahrten/hooks/useFavs.ts @@ -3,19 +3,16 @@ import type { MinimalStopPlace } from '@/types/stopPlace'; import { useCallback } from 'react'; export type Favs = Record; -export const useFavs = (key?: 'favs' | 'regionalFavs') => { - const [cookies] = useExpertCookies([key!]); - if (!key) { - return {}; - } - const savedFavs = cookies[key]; +export const useFavs = () => { + const [cookies] = useExpertCookies(['favs']); + const savedFavs = cookies.favs; return savedFavs || {}; }; -export const useFavAction = (key: 'favs' | 'regionalFavs') => { - const [_, setCookie] = useExpertCookies([key]); - const favs = useFavs(key); +export const useFavAction = () => { + const [_, setCookie] = useExpertCookies(['favs']); + const favs = useFavs(); return useCallback( (stopPlace: MinimalStopPlace) => { @@ -26,28 +23,24 @@ export const useFavAction = (key: 'favs' | 'regionalFavs') => { evaNumber: stopPlace.evaNumber, }, }; - setCookie(key, newFavs); + setCookie('favs', newFavs); return newFavs; }, - [setCookie, favs, key], + [setCookie, favs], ); }; -export const useUnfavAction = (key?: 'favs' | 'regionalFavs') => { - const [_, setCookie] = useExpertCookies([key!]); - const favs = useFavs(key); +export const useUnfavAction = () => { + const [_, setCookie] = useExpertCookies(['favs']); + const favs = useFavs(); return useCallback( (stopPlace: MinimalStopPlace) => { - if (!key) { - return undefined; - } - delete favs[stopPlace.evaNumber]; const newFavs = { ...favs }; - setCookie(key, newFavs); + setCookie('favs', newFavs); return newFavs; }, - [key, favs, setCookie], + [favs, setCookie], ); }; diff --git a/src/client/Common/Components/CheckInLink/TravelynxLink.tsx b/src/client/Common/Components/CheckInLink/TravelynxLink.tsx index de2ef1956..9a6242251 100644 --- a/src/client/Common/Components/CheckInLink/TravelynxLink.tsx +++ b/src/client/Common/Components/CheckInLink/TravelynxLink.tsx @@ -1,6 +1,7 @@ import { useCommonConfig } from '@/client/Common/provider/CommonConfigProvider'; import { stopPropagation } from '@/client/Common/stopPropagation'; -import type { CommonProductInfo, CommonStopInfo } from '@/types/HAFAS'; +import type { CommonProductInfo } from '@/types/journey'; +import type { CommonStopInfo } from '@/types/stopPlace'; import { SvgIcon, Tooltip } from '@mui/material'; import { addMinutes, isBefore } from 'date-fns'; import type { FC } from 'react'; diff --git a/src/client/Common/Components/Connections/ConnectionDisplay.tsx b/src/client/Common/Components/Connections/ConnectionDisplay.tsx index 4a0c81e42..5537828a4 100644 --- a/src/client/Common/Components/Connections/ConnectionDisplay.tsx +++ b/src/client/Common/Components/Connections/ConnectionDisplay.tsx @@ -2,7 +2,7 @@ import { Connection } from '@/client/Common/Components/Connections/Connection'; import { FullTrainName } from '@/client/Common/Components/FullTrainName'; import { Loading, LoadingType } from '@/client/Common/Components/Loading'; import { trpc } from '@/router'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; import Close from '@mui/icons-material/Close'; import { @@ -24,7 +24,7 @@ const PositionedCloseButton = styled(IconButton)` `; interface Props { - journey: ParsedSearchOnTripResponse; + journey: JourneyResponse; stop: Omit & Required>; open: boolean; toggle: (e: MouseEvent) => void; diff --git a/src/client/Common/Components/Connections/ConnectionIcon.tsx b/src/client/Common/Components/Connections/ConnectionIcon.tsx index fd2fe2c29..450db6e37 100644 --- a/src/client/Common/Components/Connections/ConnectionIcon.tsx +++ b/src/client/Common/Components/Connections/ConnectionIcon.tsx @@ -1,5 +1,5 @@ import { ConnectionDisplay } from '@/client/Common/Components/Connections/ConnectionDisplay'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; import DepartureBoard from '@mui/icons-material/DepartureBoard'; import { IconButton, Tooltip } from '@mui/material'; @@ -7,7 +7,7 @@ import { differenceInHours } from 'date-fns'; import { type FC, type MouseEvent, useCallback, useState } from 'react'; interface Props { - journey?: ParsedSearchOnTripResponse; + journey?: JourneyResponse; stop: RouteStop; className?: string; } diff --git a/src/client/Common/Components/Details/DetailsLink.tsx b/src/client/Common/Components/Details/DetailsLink.tsx index 1ab402317..53833de01 100644 --- a/src/client/Common/Components/Details/DetailsLink.tsx +++ b/src/client/Common/Components/Details/DetailsLink.tsx @@ -1,5 +1,5 @@ import { stopPropagation } from '@/client/Common/stopPropagation'; -import type { CommonProductInfo } from '@/types/HAFAS'; +import type { CommonProductInfo } from '@/types/journey'; import { Link } from '@tanstack/react-router'; import type { FC, PropsWithChildren } from 'react'; diff --git a/src/client/Common/Components/Details/Header.tsx b/src/client/Common/Components/Details/Header.tsx index fd436dbce..31611d42d 100644 --- a/src/client/Common/Components/Details/Header.tsx +++ b/src/client/Common/Components/Details/Header.tsx @@ -5,9 +5,8 @@ import { StopPlaceNameWithRl100 } from '@/client/Common/Components/StopPlaceName import { useDetails } from '@/client/Common/provider/DetailsProvider'; import ArrowBackIos from '@mui/icons-material/ArrowBackIos'; import ArrowForwardIos from '@mui/icons-material/ArrowForwardIos'; -import Map from '@mui/icons-material/Map'; import { IconButton, styled } from '@mui/material'; -import { useCallback, useMemo } from 'react'; +import { useCallback } from 'react'; import type { FC } from 'react'; import { BaseHeader } from '../BaseHeader'; @@ -61,10 +60,8 @@ const ArrowForward = styled(ArrowBack.withComponent(ArrowForwardIos))` export const Header: FC = () => { const { details, - additionalInformation, refreshDetails, trainName, - polyline, toggleMapDisplay, initialDepartureDate, sameTrainDaysInFuture, @@ -79,11 +76,6 @@ export const Header: FC = () => { sameTrainDaysInFuture(-1); }, [sameTrainDaysInFuture]); - const operatorName = useMemo( - () => additionalInformation?.operatorName || details?.train.operator?.name, - [additionalInformation, details], - ); - return ( @@ -91,7 +83,9 @@ export const Header: FC = () => { <> - {operatorName && {operatorName}} + {details?.train.operator && ( + {details?.train.operator} + )} { )} - {polyline && ( + {/* {polyline && ( { > - )} + )} */} { - const { polyline, showMarkers, toggleShowMarkers } = useDetails(); - const polylineLocations = useMemo(() => { - if (!polyline) { - return undefined; - } - if (showMarkers) { - return polyline.locations; - } - return [polyline.locations.at(-1)!, polyline.locations[0]]; - }, [polyline, showMarkers]); - const markers = useMemo( - () => - polylineLocations?.map((location, i) => { - const popup = new MapLibre.Popup().setText(location.name); - return ( - - ); - }), - [polylineLocations], - ); - if (!polyline) return null; + return null; - return ( - - - {polyline && ( - - - - )} - {markers} - - {showMarkers ? : } - - - ); + // const { polyline, showMarkers, toggleShowMarkers } = useDetails(); + // const polylineLocations = useMemo(() => { + // if (!polyline) { + // return undefined; + // } + // if (showMarkers) { + // return polyline.locations; + // } + // return [polyline.locations.at(-1)!, polyline.locations[0]]; + // }, [polyline, showMarkers]); + // const markers = useMemo( + // () => + // polylineLocations?.map((location, i) => { + // const popup = new MapLibre.Popup().setText(location.name); + // return ( + // + // ); + // }), + // [polylineLocations], + // ); + // if (!polyline) return null; + + // return ( + // + // + // {polyline && ( + // + // + // + // )} + // {markers} + // + // {showMarkers ? : } + // + // + // ); }; export default MapDisplay; diff --git a/src/client/Common/Components/Details/Messages.tsx b/src/client/Common/Components/Details/Messages.tsx index 4f212b3f7..2fd7abb72 100644 --- a/src/client/Common/Components/Details/Messages.tsx +++ b/src/client/Common/Components/Details/Messages.tsx @@ -1,8 +1,7 @@ -import type { RemL } from '@/types/HAFAS'; import type { FC } from 'react'; interface Props { - messages?: RemL[]; + messages?: string[]; } export const Messages: FC = ({ messages }) => { @@ -11,7 +10,7 @@ export const Messages: FC = ({ messages }) => { return ( <> {messages.map((m) => ( -
{m.txtN}
+
{m}
))} ); diff --git a/src/client/Common/Components/Details/Stop.cy.tsx b/src/client/Common/Components/Details/Stop.cy.tsx index 5d6a4b2c5..8ba7867e5 100644 --- a/src/client/Common/Components/Details/Stop.cy.tsx +++ b/src/client/Common/Components/Details/Stop.cy.tsx @@ -1,5 +1,5 @@ import { Stop } from '@/client/Common/Components/Details/Stop'; -import type { CommonStopInfo } from '@/types/HAFAS'; +import type { CommonStopInfo } from '@/types/stopPlace'; import type { PropsFor } from '@mui/system'; describe('Stop', () => { diff --git a/src/client/Common/Components/Details/Stop.tsx b/src/client/Common/Components/Details/Stop.tsx index e2b9d0f51..b42e336e3 100644 --- a/src/client/Common/Components/Details/Stop.tsx +++ b/src/client/Common/Components/Details/Stop.tsx @@ -8,20 +8,17 @@ import { TravelsWith } from '@/client/Common/Components/Details/TravelsWith'; import { Platform } from '@/client/Common/Components/Platform'; import { StopPlaceLink } from '@/client/Common/Components/StopPlaceLink'; import { Time } from '@/client/Common/Components/Time'; -import { useDetails } from '@/client/Common/provider/DetailsProvider'; import type { TransportDestinationRef, TransportOriginRef, } from '@/external/generated/risJourneysV2'; -import type { ParsedProduct } from '@/types/HAFAS'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { CommonProductInfo, JourneyResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; import { Stack, Tooltip, styled } from '@mui/material'; import { useCallback, useMemo } from 'react'; import type { FC, MouseEvent } from 'react'; import { CoachSequence } from '../CoachSequence/CoachSequence'; import { DetailMessages } from '../Messages/Detail'; -import { Messages } from './Messages'; const ArrivalTime = styled(Time)` grid-area: ar; @@ -127,10 +124,10 @@ const Container = styled('div')<{ ); interface Props { - journey?: ParsedSearchOnTripResponse; + journey?: JourneyResponse; stop: RouteStop; - train?: ParsedProduct; - showWR?: ParsedProduct; + train?: CommonProductInfo; + showWR?: CommonProductInfo; isPast?: boolean; initialDepartureDate?: Date; onStopClick?: (stop: RouteStop) => void; @@ -150,13 +147,7 @@ export const Stop: FC = ({ continuationBy, continuationFor, }) => { - const { additionalInformation } = useDetails(); - const occupancy = useMemo( - () => - additionalInformation?.occupancy[stop.station.evaNumber] || - stop.auslastung, - [stop, additionalInformation], - ); + const occupancy = stop.auslastung; const [platforms, showArrivalPlatform] = useMemo(() => { const platforms = { @@ -286,7 +277,6 @@ export const Stop: FC = ({ {stop.irisMessages && } - ); diff --git a/src/client/Common/Components/FullTrainName.tsx b/src/client/Common/Components/FullTrainName.tsx index 3cea42868..9aa899bf1 100644 --- a/src/client/Common/Components/FullTrainName.tsx +++ b/src/client/Common/Components/FullTrainName.tsx @@ -1,4 +1,4 @@ -import type { CommonProductInfo } from '@/types/HAFAS'; +import type { CommonProductInfo } from '@/types/journey'; import type { FC } from 'react'; type FTNPropsFallback = { diff --git a/src/client/Common/Components/StopPlaceSearch.tsx b/src/client/Common/Components/StopPlaceSearch.tsx index 8e2a9954d..a39ef2c08 100644 --- a/src/client/Common/Components/StopPlaceSearch.tsx +++ b/src/client/Common/Components/StopPlaceSearch.tsx @@ -1,6 +1,5 @@ import { useStopPlaceSearch } from '@/client/Common/hooks/useStopPlaceSearch'; import { useCommonConfig } from '@/client/Common/provider/CommonConfigProvider'; -import type { AllowedHafasProfile } from '@/types/HAFAS'; import type { MinimalStopPlace } from '@/types/stopPlace'; import { MenuItem, Paper, TextField, styled } from '@mui/material'; import { useCombobox } from 'downshift'; @@ -35,7 +34,6 @@ export interface Props { onChange: (s?: MinimalStopPlace) => any; autoFocus?: boolean; placeholder?: string; - profile?: AllowedHafasProfile; maxSuggestions?: number; filterForIris?: boolean; groupedBySales?: boolean; diff --git a/src/client/Common/Components/Zugsuche.tsx b/src/client/Common/Components/Zugsuche.tsx index f3dc181cb..a5bb1dba4 100644 --- a/src/client/Common/Components/Zugsuche.tsx +++ b/src/client/Common/Components/Zugsuche.tsx @@ -1,6 +1,6 @@ import { ZugsucheAutocomplete } from '@/client/Common/Components/ZugsucheAutocomplete'; import { stopPropagation } from '@/client/Common/stopPropagation'; -import type { ParsedJourneyMatchResponse } from '@/types/HAFAS/JourneyMatch'; +import type { JourneyFindResponse } from '@/types/journey'; import Today from '@mui/icons-material/Today'; import Train from '@mui/icons-material/Train'; import { @@ -67,7 +67,7 @@ export const Zugsuche: FC = ({ children }) => { [open], ); const submit = useCallback( - (match: ParsedJourneyMatchResponse | null) => { + (match: JourneyFindResponse | null) => { if (match) { toggleModal(); toggleDrawer(); diff --git a/src/client/Common/Components/ZugsucheAutocomplete.tsx b/src/client/Common/Components/ZugsucheAutocomplete.tsx index cf4fe9b73..5b4644494 100644 --- a/src/client/Common/Components/ZugsucheAutocomplete.tsx +++ b/src/client/Common/Components/ZugsucheAutocomplete.tsx @@ -1,6 +1,6 @@ import { Loading, LoadingType } from '@/client/Common/Components/Loading'; import { trpc } from '@/router'; -import type { ParsedJourneyMatchResponse } from '@/types/HAFAS/JourneyMatch'; +import type { JourneyFindResponse } from '@/types/journey'; import { MenuItem, Paper, TextField, styled } from '@mui/material'; import debounce from 'debounce-promise'; import { useCombobox } from 'downshift'; @@ -42,18 +42,15 @@ const StyledMenuItem = styled(MenuItem)` interface Props { initialDeparture?: Date; withOEV?: boolean; - onChange: (match: ParsedJourneyMatchResponse | null) => any; + onChange: (match: JourneyFindResponse | null) => any; } -const itemToString = (j: ParsedJourneyMatchResponse | null) => - j?.train.name || ''; +const itemToString = (j: JourneyFindResponse | null) => j?.train.name || ''; export const ZugsucheAutocomplete: FC = ({ initialDeparture = new Date(), onChange: onSelectedItemChange, withOEV, }) => { - const [suggestions, setSuggestions] = useState( - [], - ); + const [suggestions, setSuggestions] = useState([]); const inputRef = useRef(null); const [loading, setLoading] = useState(0); const trpcUtils = trpc.useUtils(); diff --git a/src/client/Common/Storage.ts b/src/client/Common/Storage.ts index 25731efe2..80d1ea999 100644 --- a/src/client/Common/Storage.ts +++ b/src/client/Common/Storage.ts @@ -14,7 +14,6 @@ const setCookieOptions: CookieSetOptions = { export interface WebConfigMap extends CommonConfig, RoutingSettings { readonly selectedDetail: string; - readonly regionalFavs: Favs; readonly favs: Favs; readonly rfavs: RoutingFavs; readonly defaultFilter: string[]; diff --git a/src/client/Common/provider/DetailsProvider.tsx b/src/client/Common/provider/DetailsProvider.tsx index 655b9bc0c..3566dce07 100644 --- a/src/client/Common/provider/DetailsProvider.tsx +++ b/src/client/Common/provider/DetailsProvider.tsx @@ -1,9 +1,6 @@ import { useCommonConfig } from '@/client/Common/provider/CommonConfigProvider'; import constate from '@/constate'; import { trpc } from '@/router'; -import type { HafasStation, ParsedPolyline } from '@/types/HAFAS'; -import type { AdditionalJourneyInformation } from '@/types/HAFAS/JourneyDetails'; -import type { RouteAuslastung, RouteStop } from '@/types/routing'; import { useNavigate } from '@tanstack/react-router'; import { addDays } from 'date-fns'; import { useCallback, useEffect, useMemo, useState } from 'react'; @@ -53,9 +50,6 @@ const useInnerDetails = ({ journeyId, jid, }); - - const [additionalInformation, setAdditionalInformation] = - useState(); const navigate = useNavigate(); const trpcUtils = trpc.useUtils(); @@ -74,7 +68,6 @@ const useInnerDetails = ({ administration: newAdministration, }, }); - setAdditionalInformation(undefined); }, [initialDepartureDate, details, administration, navigate, trainName], ); @@ -83,39 +76,6 @@ const useInnerDetails = ({ if (!details) { return; } - const fetchAdditional = async () => { - if (details.journeyId) { - try { - setAdditionalInformation( - await trpcUtils.hafas.additionalInformation.fetch( - { - trainName, - journeyId: details.journeyId, - initialDepartureDate, - evaNumberAlongRoute: - details.stops[details.stops.length - 1].station.evaNumber, - }, - { - staleTime: Number.POSITIVE_INFINITY, - }, - ), - ); - } catch { - // ignoring this - } - } else { - const occupancy: Record = {}; - for (const s of details.stops) { - if (s.auslastung) { - occupancy[s.station.evaNumber] = s.auslastung; - } - } - setAdditionalInformation({ - occupancy, - }); - } - }; - fetchAdditional(); if (details.journeyId) { navigate({ to: '/details/$train', @@ -129,7 +89,7 @@ const useInnerDetails = ({ from: '/details/$train', }); } - }, [details, trainName, initialDepartureDate, trpcUtils, navigate]); + }, [details, trainName, navigate]); useEffect(() => { let intervalId: NodeJS.Timeout; @@ -156,39 +116,13 @@ const useInnerDetails = ({ setShowMarkers((old) => !old); }, []); - const matchedPolyline: - | (Omit & { - locations: (HafasStation & { - details?: RouteStop; - })[]; - }) - | undefined = useMemo(() => { - const polyline = additionalInformation?.polyline || details?.polyline; - if (!polyline) return undefined; - if (!details) return undefined; - - for (const loc of polyline.locations) { - const detailsLoc = details.stops.find( - (s) => s.station.evaNumber === loc.evaNumber, - ); - if (detailsLoc) { - // @ts-expect-error adding information - loc.details = detailsLoc; - } - } - - return polyline; - }, [details, additionalInformation]); - return { initialDepartureDate, trainName, details, isFetching, - additionalInformation, error, refreshDetails: refetchDetails, - polyline: matchedPolyline, isMapDisplay, toggleMapDisplay, showMarkers, diff --git a/src/client/Routing/Components/RouteList/SegmentTrainComponent/WalkSegmentTrain.tsx b/src/client/Routing/Components/RouteList/SegmentTrainComponent/WalkSegmentTrain.tsx index fc48001aa..82c83787f 100644 --- a/src/client/Routing/Components/RouteList/SegmentTrainComponent/WalkSegmentTrain.tsx +++ b/src/client/Routing/Components/RouteList/SegmentTrainComponent/WalkSegmentTrain.tsx @@ -9,7 +9,10 @@ interface Props { } export const WalkSegmentTrain: FC = ({ segment, className }) => { - const mapsLink = `https://www.google.com/maps/dir/?api=1&origin=${segment.segmentStart.coordinates.lat},${segment.segmentStart.coordinates.lng}&destination=${segment.segmentDestination.coordinates.lat},${segment.segmentDestination.coordinates.lng}&travelmode=walking`; + if (!segment.segmentDestination.position || !segment.segmentStart.position) { + return null; + } + const mapsLink = `https://www.google.com/maps/dir/?api=1&origin=${segment.segmentStart.position.latitude},${segment.segmentStart.position.longitude}&destination=${segment.segmentDestination.position.latitude},${segment.segmentDestination.position.longitude}&travelmode=walking`; return (
diff --git a/src/client/Routing/Components/Search/BC100Disclaimer.tsx b/src/client/Routing/Components/Search/BC100Disclaimer.tsx deleted file mode 100644 index 95edfa88f..000000000 --- a/src/client/Routing/Components/Search/BC100Disclaimer.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Dialog, DialogContent, DialogTitle } from '@mui/material'; -import { useState } from 'react'; -import type { FC } from 'react'; - -export const BC100Disclaimer: FC = () => { - const [open, setOpen] = useState(true); - return ( - setOpen(false)} - > - BC100 Disclaimer - - Dieser Filter funktioniert NICHT mit "bahn.de" als Backend!
- Dieser Filter nimmt alle Fahrten raus welche mit "DB-Fahrscheine gelten - nicht" markiert sind.
- Ganz konkret basiert es auf dem Attribut "DU". Keine Garantie das nicht - doch Verbindungen ausgegeben werden welche nicht gefahren werden dürfen! - Bitte informiert euch selbstständig! -
-
- ); -}; diff --git a/src/client/Routing/Components/Search/NetzcardDisclaimer.tsx b/src/client/Routing/Components/Search/NetzcardDisclaimer.tsx deleted file mode 100644 index b0dba1e49..000000000 --- a/src/client/Routing/Components/Search/NetzcardDisclaimer.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Dialog, DialogContent, DialogTitle } from '@mui/material'; -import { useState } from 'react'; -import type { FC } from 'react'; - -export const NetzcardDisclaimer: FC = () => { - const [open, setOpen] = useState(true); - return ( - setOpen(false)} - > - Netzcard Disclaimer - - Dieser Filter funktioniert NICHT mit "bahn.de" als Backend!
- Dies ist ein experimenteller Filter um möglichst nur Verbindungen zu - suchen welche mit der Netzcard erlaubt sind. -
- USE AT YOUR OWN RISK! -
- Aktuell werden nur EVUs der DB AG berücksichtigt. Die aktuelle Liste - findet sich hier:{' '} - - NetzcardBetreiber.json - {' '} -
- Das bedeutet vor allem das die Ausnahmen für bestimmte Strecken NICHT - berücksichtigt werden! -
-
- ); -}; diff --git a/src/client/Routing/Components/Search/SettingsPanel.tsx b/src/client/Routing/Components/Search/SettingsPanel.tsx index 83fa2a01a..81b3f596f 100644 --- a/src/client/Routing/Components/Search/SettingsPanel.tsx +++ b/src/client/Routing/Components/Search/SettingsPanel.tsx @@ -1,11 +1,8 @@ -import { BC100Disclaimer } from '@/client/Routing/Components/Search/BC100Disclaimer'; -import { NetzcardDisclaimer } from '@/client/Routing/Components/Search/NetzcardDisclaimer'; import { useRoutingConfigActions, useRoutingSettings, } from '@/client/Routing/provider/RoutingConfigProvider'; import type { RoutingSettings } from '@/client/Routing/provider/RoutingConfigProvider'; -import { AllowedHafasProfile } from '@/types/HAFAS'; import AllInclusive from '@mui/icons-material/AllInclusive'; import Cached from '@mui/icons-material/Cached'; import ExpandMore from '@mui/icons-material/ExpandMore'; @@ -17,10 +14,7 @@ import { AccordionSummary, Badge, Chip, - FormControl, FormControlLabel, - MenuItem, - Select, Stack, Switch, TextField, @@ -28,7 +22,7 @@ import { styled, } from '@mui/material'; import type { SelectChangeEvent } from '@mui/material'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; import type { ChangeEvent, FC } from 'react'; const StyledAccordion = styled(Accordion)` @@ -57,8 +51,6 @@ const inputCss = css` export const SettingsPanel: FC = () => { const settings = useRoutingSettings(); - const [showNetzcardDisclaimer, setShowNetzcardDisclaimer] = useState(false); - const [showBC100Disclaimer, setShowBC100Disclaimer] = useState(false); const { updateSettings } = useRoutingConfigActions(); const handleInputChange = useCallback( (key: keyof RoutingSettings) => (e: ChangeEvent) => @@ -71,21 +63,6 @@ export const SettingsPanel: FC = () => { }, [updateSettings], ); - const handleNetzcard = useCallback( - (_: unknown, checked: boolean) => { - updateSettings('onlyNetzcard', checked); - setShowNetzcardDisclaimer(checked); - }, - [updateSettings], - ); - - const handleBC100 = useCallback( - (_: unknown, checked: boolean) => { - updateSettings('onlyBC100', checked); - setShowBC100Disclaimer(checked); - }, - [updateSettings], - ); const handleHafasProfile = useCallback( (event: SelectChangeEvent) => { @@ -104,23 +81,12 @@ export const SettingsPanel: FC = () => { }, [settings.maxChanges]); let filterLabel = 'Alle Zuege'; - if (settings.onlyNetzcard && settings.onlyRegional) { - filterLabel = 'Nahverkehr (Netzcard)'; - } - if (settings.onlyNetzcard) { - filterLabel = 'Netzcard'; - } if (settings.onlyRegional) { filterLabel = 'Nahverkehr'; } - if (settings.onlyBC100) { - filterLabel = 'BC100'; - } return ( <> - {showNetzcardDisclaimer && } - {showBC100Disclaimer && } { } label="Nur Regionalzüge" /> - - - bahn.de - - Business DB Navigator (Legacy) - - - OEBB Scotty - - - } - label="Datenlieferant" - /> - - - - } - label="Netzcard erlaubt" - /> - - - } - label="BC100 erlaubt" - /> diff --git a/src/client/Routing/provider/RoutingConfigProvider.tsx b/src/client/Routing/provider/RoutingConfigProvider.tsx index 1184d0e60..b52d0c35a 100644 --- a/src/client/Routing/provider/RoutingConfigProvider.tsx +++ b/src/client/Routing/provider/RoutingConfigProvider.tsx @@ -1,6 +1,5 @@ import { useExpertCookies } from '@/client/Common/hooks/useExpertCookies'; import constate from '@/constate'; -import { AllowedHafasProfile } from '@/types/HAFAS'; import type { MinimalStopPlace } from '@/types/stopPlace'; import { useCallback, useMemo, useState } from 'react'; import type { PropsWithChildren, SyntheticEvent } from 'react'; @@ -9,19 +8,11 @@ export interface RoutingSettings { maxChanges: string; transferTime: string; onlyRegional: boolean; - onlyNetzcard: boolean; - onlyBC100: boolean; - hafasProfileN?: - | AllowedHafasProfile.DB - | AllowedHafasProfile.OEBB - | AllowedHafasProfile.BAHN; } const routingConfigKeys = [ 'maxChanges', 'transferTime', 'onlyRegional', - 'onlyNetzcard', - 'hafasProfileN', ] as (keyof RoutingSettings)[]; const useRoutingConfigInternal = ({ @@ -160,9 +151,6 @@ export const RoutingConfigProvider: FCC<{ maxChanges: routingConfig.maxChanges?.toString() ?? '-1', transferTime: routingConfig.transferTime?.toString() ?? '0', onlyRegional: routingConfig.onlyRegional ?? false, - onlyNetzcard: routingConfig.onlyNetzcard ?? false, - onlyBC100: routingConfig.onlyBC100 ?? false, - hafasProfileN: routingConfig.hafasProfileN ?? AllowedHafasProfile.BAHN, }; return ( diff --git a/src/client/Routing/provider/useFetchRouting.ts b/src/client/Routing/provider/useFetchRouting.ts index 0780c240e..4d03a1c66 100644 --- a/src/client/Routing/provider/useFetchRouting.ts +++ b/src/client/Routing/provider/useFetchRouting.ts @@ -6,7 +6,6 @@ import { useRouting } from '@/client/Routing/provider/RoutingProvider'; import { useRoutingNavigate } from '@/client/Routing/util'; import { uniqBy } from '@/client/util'; import { trpc } from '@/router'; -import { AllowedHafasProfile } from '@/types/HAFAS'; import type { RoutingResult } from '@/types/routing'; import type { MinimalStopPlace } from '@/types/stopPlace'; import { useCallback } from 'react'; @@ -65,44 +64,26 @@ export const useFetchRouting = () => { setRoutes(undefined); let routingResult: RoutingResult; try { - if (routeSettings.hafasProfileN !== AllowedHafasProfile.BAHN) { - routingResult = await trpcUtils.hafas.tripSearch.fetch( - { - time: touchedDate ? date : new Date(), - searchForDeparture: departureMode === 'ab', - ...routeSettings, - }, - { - // @ts-expect-error needs https://github.com/trpc/trpc/issues/6028 - trpc: { - context: { - skipBatch: true, - }, - }, - }, - ); - } else { - routingResult = await trpcUtils.bahn.routing.fetch( - { - time: touchedDate ? date : new Date(), - searchForDeparture: departureMode === 'ab', - maxChanges: routeSettings.maxChanges, - start: routeSettings.start, - destination: routeSettings.destination, - onlyRegional: routeSettings.onlyRegional, - transferTime: routeSettings.transferTime, - via: routeSettings.via, - }, - { - // @ts-expect-error needs https://github.com/trpc/trpc/issues/6028 - trpc: { - context: { - skipBatch: true, - }, + routingResult = await trpcUtils.bahn.routing.fetch( + { + time: touchedDate ? date : new Date(), + searchForDeparture: departureMode === 'ab', + maxChanges: routeSettings.maxChanges, + start: routeSettings.start, + destination: routeSettings.destination, + onlyRegional: routeSettings.onlyRegional, + transferTime: routeSettings.transferTime, + via: routeSettings.via, + }, + { + // @ts-expect-error needs https://github.com/trpc/trpc/issues/6028 + trpc: { + context: { + skipBatch: true, }, }, - ); - } + }, + ); setRoutes(routingResult.routes); setEarlierContext(routingResult.context.earlier); @@ -112,7 +93,6 @@ export const useFetchRouting = () => { } }, [ - trpcUtils.hafas.tripSearch, trpcUtils.bahn.routing, date, setEarlierContext, @@ -146,44 +126,27 @@ export const useFetchRouting = () => { let routingResult: RoutingResult; try { - if (routeSettings.hafasProfileN === AllowedHafasProfile.BAHN) { - routingResult = await trpcUtils.bahn.routing.fetch( - { - time: touchedDate ? date : new Date(), - searchForDeparture: departureMode === 'ab', - maxChanges: routeSettings.maxChanges, - start: routeSettings.start, - destination: routeSettings.destination, - onlyRegional: routeSettings.onlyRegional, - transferTime: routeSettings.transferTime, - via: routeSettings.via, - ctxScr: type === 'earlier' ? earlierContext : laterContext, - }, - { - // @ts-expect-error needs https://github.com/trpc/trpc/issues/6028 - trpc: { - context: { - skipBatch: true, - }, - }, - }, - ); - } else { - routingResult = await trpcUtils.hafas.tripSearch.fetch( - { - ctxScr: type === 'earlier' ? earlierContext : laterContext, - ...routeSettings, - }, - { - // @ts-expect-error needs https://github.com/trpc/trpc/issues/6028 - trpc: { - context: { - skipBatch: true, - }, + routingResult = await trpcUtils.bahn.routing.fetch( + { + time: touchedDate ? date : new Date(), + searchForDeparture: departureMode === 'ab', + maxChanges: routeSettings.maxChanges, + start: routeSettings.start, + destination: routeSettings.destination, + onlyRegional: routeSettings.onlyRegional, + transferTime: routeSettings.transferTime, + via: routeSettings.via, + ctxScr: type === 'earlier' ? earlierContext : laterContext, + }, + { + // @ts-expect-error needs https://github.com/trpc/trpc/issues/6028 + trpc: { + context: { + skipBatch: true, }, }, - ); - } + }, + ); setRoutes((oldRoutes = []) => { const newRoutes = @@ -204,7 +167,6 @@ export const useFetchRouting = () => { } }, [ - trpcUtils.hafas.tripSearch, trpcUtils.bahn.routing, date, touchedDate, diff --git a/src/external/risJourneys.ts b/src/external/risJourneys.ts index 85d0f1a41..9d59ddc7a 100644 --- a/src/external/risJourneys.ts +++ b/src/external/risJourneys.ts @@ -8,10 +8,9 @@ import type { import type { StopPlaceEmbedded } from '@/external/generated/risJourneysV2'; import { sortJourneys } from '@/external/risJourneysV2'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { logger } from '@/server/logger'; -import type { ParsedProduct } from '@/types/HAFAS'; -import type { ParsedJourneyMatchResponse } from '@/types/HAFAS/JourneyMatch'; +import type { CommonProductInfo, JourneyFindResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; import axios from 'axios'; import { differenceInHours, format, isEqual } from 'date-fns'; @@ -26,9 +25,9 @@ const risJourneysConfiguration = new RisJourneysConfiguration({ }, }); -const journeyFindCache = new Cache(CacheDatabase.JourneyFind); +const journeyFindCache = getCache(CacheDatabase.JourneyFind); -const journeyCache = new Cache(CacheDatabase.Journey); +const journeyCache = getCache(CacheDatabase.Journey); logger.info( `using ${process.env.RIS_JOURNEYS_USER_AGENT} as RIS::Journeys UserAgent`, @@ -55,7 +54,9 @@ const trainTypes: TransportType[] = [ TransportType.CityTrain, ]; -const mapTransportToTrain = (transport: TransportPublic): ParsedProduct => ({ +const mapTransportToTrain = ( + transport: TransportPublic, +): CommonProductInfo => ({ name: `${transport.category} ${ longDistanceTypes.includes(transport.type) ? transport.number @@ -73,9 +74,9 @@ const mapStationShortToRouteStops = ( station, }); -function mapToParsedJourneyMatchResponse( +function mapToJourneyFindResponse( journeyMatch: JourneyMatch, -): ParsedJourneyMatchResponse { +): JourneyFindResponse { return { // Technically wrong! jid: journeyMatch.journeyID, @@ -186,10 +187,10 @@ export async function findJourneyHafasCompatible( date: Date, category?: string, withOEV?: boolean, -): Promise { +): Promise { const risResult = await findJourney(trainNumber, date, category, withOEV); - return risResult.map(mapToParsedJourneyMatchResponse); + return risResult.map(mapToJourneyFindResponse); } export async function getJourneyDetails( diff --git a/src/external/risJourneysV2.ts b/src/external/risJourneysV2.ts index 36cd4803e..3ba894d35 100644 --- a/src/external/risJourneysV2.ts +++ b/src/external/risJourneysV2.ts @@ -12,9 +12,8 @@ import type { } from '@/external/generated/risJourneysV2'; import { getStopPlaceByEva } from '@/server/StopPlace/search'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; -import type { ParsedProduct } from '@/types/HAFAS'; -import type { ParsedJourneyMatchResponse } from '@/types/HAFAS/JourneyMatch'; +import { CacheDatabase, getCache } from '@/server/cache'; +import type { CommonProductInfo, JourneyFindResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; import axios from 'axios'; import { format, isBefore, isEqual, isSameDay, subDays } from 'date-fns'; @@ -39,11 +38,9 @@ const client = new JourneysApi( axiosWithTimeout, ); -const journeyFindCache = new Cache( - CacheDatabase.JourneyFindV2, -); +const journeyFindCache = getCache(CacheDatabase.JourneyFindV2); -const journeyCache = new Cache(CacheDatabase.JourneyV2); +const journeyCache = getCache(CacheDatabase.JourneyV2); export const health = { has401: false, @@ -57,7 +54,7 @@ const trainTypes: string[] = [ 'CITY_TRAIN', ]; -const mapTransportToTrain = (transport: Transport): ParsedProduct => ({ +const mapTransportToTrain = (transport: Transport): CommonProductInfo => ({ name: `${transport.category} ${ longDistanceTypes.includes(transport.type) ? transport.journeyNumber @@ -127,9 +124,9 @@ export function sortJourneys( return 0; } -function mapToParsedJourneyMatchResponse( +function mapToJourneyFindResponse( journeyFindResult: JourneyFindResult, -): ParsedJourneyMatchResponse { +): JourneyFindResponse { return { // Technically wrong! jid: journeyFindResult.journeyID, @@ -278,10 +275,10 @@ export async function findJourneyHafasCompatible( date: Date, category?: string, withOEV?: boolean, -): Promise { +): Promise { const risResult = await findJourney(trainNumber, date, category, withOEV); - return Promise.all(risResult.map(mapToParsedJourneyMatchResponse)); + return Promise.all(risResult.map(mapToJourneyFindResponse)); } export async function getJourneyDetails( diff --git a/src/external/risMaps.ts b/src/external/risMaps.ts index 9c81201f8..72cb15492 100644 --- a/src/external/risMaps.ts +++ b/src/external/risMaps.ts @@ -1,10 +1,6 @@ -import { - Configuration, - type VehicleLayoutFeatureCollection, - VehicleLayoutsApi, -} from '@/external/generated/risMaps'; +import { Configuration, VehicleLayoutsApi } from '@/external/generated/risMaps'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import axios, { isAxiosError } from 'axios'; const risConnectionsConfiguration = new Configuration({ @@ -29,9 +25,7 @@ const mapsClient = new VehicleLayoutsApi( axiosWithTimeout, ); -const cache = new Cache( - CacheDatabase.VehicleLayoutsMaps, -); +const cache = getCache(CacheDatabase.VehicleLayoutsMaps); export async function getVehicleLayout(vehicleId: string) { if (await cache.exists(vehicleId)) { diff --git a/src/external/risStations.ts b/src/external/risStations.ts index 3a6a0d74a..a25bd9f75 100644 --- a/src/external/risStations.ts +++ b/src/external/risStations.ts @@ -12,6 +12,7 @@ import type { StopPlaceSearchResult, } from '@/external/types'; import { axiosUpstreamInterceptor } from '@/server/admin'; +// import { axiosUpstreamInterceptor } from '@/server/admin'; import axios from 'axios'; const risStationsConfiguration = new RisStationsConfiguration({ diff --git a/src/external/risTransports/occupancy.ts b/src/external/risTransports/occupancy.ts index d34b3e159..d859daf69 100644 --- a/src/external/risTransports/occupancy.ts +++ b/src/external/risTransports/occupancy.ts @@ -1,10 +1,7 @@ -import { - type JourneyOccupancy, - OccupanciesApi, -} from '@/external/generated/risTransports'; +import { OccupanciesApi } from '@/external/generated/risTransports'; import { risTransportsConfiguration } from '@/external/risTransports/config'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { Temporal } from '@js-temporal/polyfill'; import Axios from 'axios'; const axiosWithTimeout = Axios.create({ @@ -39,9 +36,7 @@ if (process.env.NODE_ENV === 'production') { ); } -const occupancyCache = new Cache( - CacheDatabase.TransportsOccupancy, -); +const occupancyCache = getCache(CacheDatabase.TransportsOccupancy); export async function getJourneyOccupancy({ journeyId, diff --git a/src/external/risTransports/sequence.ts b/src/external/risTransports/sequence.ts index 2dcd2468e..c05f53b17 100644 --- a/src/external/risTransports/sequence.ts +++ b/src/external/risTransports/sequence.ts @@ -5,7 +5,7 @@ import { risTransportsConfiguration, } from '@/external/risTransports/config'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { logger } from '@/server/logger'; import { Temporal } from '@js-temporal/polyfill'; import Axios from 'axios'; @@ -47,7 +47,7 @@ if (process.env.NODE_ENV === 'production') { ); } -const negativeHitCache = new Cache(CacheDatabase.NegativeNewSequence); +const negativeHitCache = getCache(CacheDatabase.NegativeNewSequence); export async function getDepartureSequence( trainCategory: string, diff --git a/src/external/risTransports/vehicles.ts b/src/external/risTransports/vehicles.ts index 110bf512f..81535cde8 100644 --- a/src/external/risTransports/vehicles.ts +++ b/src/external/risTransports/vehicles.ts @@ -1,14 +1,11 @@ import { uniqBy } from '@/client/util'; -import { - type MatchVehicleID, - VehiclesApi, -} from '@/external/generated/risTransports'; +import { VehiclesApi } from '@/external/generated/risTransports'; import { isWithin20Hours, risTransportsConfiguration, } from '@/external/risTransports/config'; import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { journeyDetails } from '@/server/journeys/v2/journeyDetails'; import Axios from 'axios'; import { addDays, format, isBefore, subDays } from 'date-fns'; @@ -19,13 +16,7 @@ const axiosWithTimeout = Axios.create({ axiosUpstreamInterceptor(axiosWithTimeout, 'vehicles-risTransports'); -type CacheType = { - previousJourneys: MatchVehicleID[]; - nextJourneys: MatchVehicleID[]; -}; -const journeyForVehiclesCache = new Cache( - CacheDatabase.JourneysForVehicle, -); +const journeyForVehiclesCache = getCache(CacheDatabase.JourneysForVehicle); const vehiclesClient = new VehiclesApi( risTransportsConfiguration, diff --git a/src/routes/_abfahrten/index.tsx b/src/routes/_abfahrten/index.tsx index 184f10ca9..d0f62f246 100644 --- a/src/routes/_abfahrten/index.tsx +++ b/src/routes/_abfahrten/index.tsx @@ -6,5 +6,5 @@ export const Route = createFileRoute('/_abfahrten/')({ }); function RouteComponent() { - return ; + return ; } diff --git a/src/routes/routing/index.tsx b/src/routes/routing/index.tsx index 04e8e1297..683c0773d 100644 --- a/src/routes/routing/index.tsx +++ b/src/routes/routing/index.tsx @@ -16,11 +16,15 @@ export const routingLoader = async (ctx: any) => { } if (ctx.params.date) { const dateNumber = +ctx.params.date; - promises.push( - Promise.resolve( - new Date(Number.isNaN(dateNumber) ? ctx.params.date : dateNumber), - ), - ); + if (dateNumber) { + promises.push( + Promise.resolve( + new Date(Number.isNaN(dateNumber) ? ctx.params.date : dateNumber), + ), + ); + } else { + promises.push(Promise.resolve(undefined)); + } } if (ctx.params.via) { const viaStations: string[] = ctx.params.via.split('|').filter(Boolean); diff --git a/src/server/HAFAS/Detail.ts b/src/server/HAFAS/Detail.ts deleted file mode 100644 index eb8455074..000000000 --- a/src/server/HAFAS/Detail.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { HafasJourneyDetails } from '@/server/HAFAS/JourneyDetails'; -import JourneyMatch from '@/server/HAFAS/JourneyMatch'; -import createCtxRecon from '@/server/HAFAS/helper/createCtxRecon'; -import { addIrisMessagesToDetails } from '@/server/journeys/journeyDetails'; -import { AllowedHafasProfile } from '@/types/HAFAS'; -import type { ParsedJourneyDetails } from '@/types/HAFAS/JourneyDetails'; -import type { ParsedJourneyMatchResponse } from '@/types/HAFAS/JourneyMatch'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; -import type { RouteStop } from '@/types/routing'; -import { isAfter } from 'date-fns'; -import searchOnTrip from './SearchOnTrip'; - -export function calculateCurrentStopPlace( - segment: ParsedSearchOnTripResponse, - currentStopId?: string, -): RouteStop | undefined { - const currentDate = Date.now(); - let currentStop: RouteStop | undefined; - - if (currentStopId) { - currentStop = segment.stops.find( - (s) => s.station.evaNumber === currentStopId, - ); - } - - if (!currentStop) { - currentStop = segment.stops.find((s) => { - const stopInfo = - s.departure && !s.departure.cancelled ? s.departure : s.arrival; - - return ( - stopInfo && !stopInfo.cancelled && isAfter(stopInfo.time, currentDate) - ); - }); - } - - return currentStop; -} - -const filterByAdministration = ( - journeys: ParsedJourneyMatchResponse[], - administration?: string, -) => { - if (!administration) return journeys; - return journeys.filter( - (j) => j.train.admin?.replaceAll('_', '') === administration, - ); -}; - -export default async ( - trainName: string, - currentStopId?: string, - station?: string, - date: Date = new Date(), - plainDetails = false, - hafasProfile: AllowedHafasProfile = AllowedHafasProfile.DB, - administration?: string, - jid?: string, -): Promise => { - let journeyDetails: ParsedJourneyDetails | undefined; - if (!jid) { - let possibleTrains: ParsedJourneyMatchResponse[] = []; - - possibleTrains = await JourneyMatch( - { - trainName, - initialDepartureDate: date, - }, - hafasProfile, - ); - possibleTrains = filterByAdministration(possibleTrains, administration); - - if (!possibleTrains.length) { - return undefined; - } - - if (station) { - while (!journeyDetails && possibleTrains.length) { - const currentTrain = possibleTrains.shift()!; - const maybeJourneyDetails = await HafasJourneyDetails( - currentTrain.jid, - hafasProfile, - ); - if (!maybeJourneyDetails) { - break; - } - for (const stop of maybeJourneyDetails.stops) { - if (stop.station.evaNumber === station) { - journeyDetails = maybeJourneyDetails; - break; - } - } - } - } else { - journeyDetails = await HafasJourneyDetails( - possibleTrains[0].jid, - hafasProfile, - ); - } - } else { - journeyDetails = await HafasJourneyDetails(jid, hafasProfile); - } - - if (!journeyDetails) return undefined; - - let relevantSegment: ParsedSearchOnTripResponse = { - type: 'JNY', - cancelled: journeyDetails.stops.every((s) => s.cancelled), - finalDestination: journeyDetails.lastStop.station.name, - jid: journeyDetails.jid, - train: journeyDetails.train, - segmentDestination: journeyDetails.lastStop.station, - segmentStart: journeyDetails.firstStop.station, - stops: journeyDetails.stops, - messages: journeyDetails.messages, - arrival: journeyDetails.lastStop.arrival, - departure: journeyDetails.firstStop.departure, - polyline: journeyDetails.polylines?.[0], - }; - if (plainDetails) { - return relevantSegment; - } - - try { - const route = await searchOnTrip( - { - ctxRecon: createCtxRecon({ - firstStop: journeyDetails.firstStop, - lastStop: journeyDetails.lastStop, - trainName: journeyDetails.train.name, - messages: journeyDetails.messages, - }), - sotMode: 'RC', - }, - hafasProfile, - ); - - relevantSegment = route.segments.find((s) => s.type === 'JNY')!; - } catch { - // we keep using the JourneyDetailsOne - } - - if (relevantSegment.stops.length !== journeyDetails.stops.length) { - for (const [index, stop] of journeyDetails.stops.entries()) { - if (stop.additional) { - relevantSegment.stops.splice(index, 0, stop); - } - } - } - - const lastStop = relevantSegment.stops - .filter((s) => s.arrival && !s.arrival.cancelled) - .pop(); - - if (currentStopId) { - relevantSegment.currentStop = relevantSegment.stops.find( - (s) => s.station.evaNumber === currentStopId, - ); - } - - if (lastStop?.arrival?.delay == null) { - for (const [index, stop] of relevantSegment.stops.entries()) { - const jDetailStop = journeyDetails.stops[index]; - - if (jDetailStop.station.evaNumber !== stop.station.evaNumber) continue; - if (jDetailStop.arrival && stop.arrival) { - stop.arrival.delay = jDetailStop.arrival.delay; - stop.arrival.time = jDetailStop.arrival.time; - } - if (jDetailStop.departure && stop.departure) { - stop.departure.delay = jDetailStop.departure.delay; - stop.departure.time = jDetailStop.departure.time; - } - } - } - - relevantSegment.currentStop = calculateCurrentStopPlace( - relevantSegment, - currentStopId, - ); - - await addIrisMessagesToDetails(relevantSegment); - - return relevantSegment; -}; diff --git a/src/server/HAFAS/HimSearch.ts b/src/server/HAFAS/HimSearch.ts deleted file mode 100644 index da8e1d3fe..000000000 --- a/src/server/HAFAS/HimSearch.ts +++ /dev/null @@ -1,93 +0,0 @@ -import makeRequest from '@/server/HAFAS/Request'; -import parseTime from '@/server/HAFAS/helper/parseTime'; -import { Cache, CacheDatabase } from '@/server/cache'; -import { logger } from '@/server/logger'; -import type { - AllowedHafasProfile, - HafasResponse, - ParsedCommon, -} from '@/types/HAFAS'; -import type { - HimMessage, - HimSearchRequest, - HimSearchResponse, - ParsedHimMessage, - ParsedHimSearchResponse, -} from '@/types/HAFAS/HimSearch'; -import { parse } from 'date-fns'; - -const parseHimMessage = (himMessage: HimMessage, common: ParsedCommon) => { - const head = himMessage.head.replaceAll(` (Quelle: ${himMessage.comp})`, ''); - return { - ...himMessage, - head: head.endsWith('.') ? head.slice(0, -1) : head, - affectedProducts: - himMessage.affProdRefL?.map((prodRef) => common.prodL[prodRef]) ?? [], - startTime: parseTime( - parse(himMessage.sDate, 'yyyyMMdd', Date.now()), - himMessage.sTime, - ), - endTime: parseTime( - parse(himMessage.eDate, 'yyyyMMdd', Date.now()), - himMessage.eTime, - ), - fromStopPlace: common.locL[himMessage.fLocX], - toStopPlace: common.locL[himMessage.tLocX], - }; -}; -const parseHimSearch = ( - d: HafasResponse, - common: ParsedCommon, -): Promise => { - return Promise.resolve({ - messages: - d.svcResL[0].res?.msgL?.map((m) => parseHimMessage(m, common)) ?? [], - }); -}; - -const HimSearch = ( - request: HimSearchRequest['req'], - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise => { - const req: HimSearchRequest = { - req: request, - meth: 'HimSearch', - }; - return makeRequest(req, raw ? undefined : parseHimSearch, profile); -}; - -const himMessageCache = new Cache(CacheDatabase.HIMMessage); - -const maxNum = Number.parseInt(process.env.HIM_MAX_FETCH || '5000', 10); - -async function fetchTodaysHimMessages() { - try { - logger.debug('Fetching HimMessages'); - const messages = await HimSearch({ - onlyToday: true, - maxNum, - }); - - if (!messages.messages) return; - - for (const message of messages.messages) { - await himMessageCache.set(message.hid, message); - } - logger.debug(`fetched ${messages.messages.length} HIM Messages`); - } catch { - logger.error('HIM Messages fetch failed'); - } -} - -// if (process.env.NODE_ENV !== 'test') { -// void fetchTodaysHimMessages(); -// setInterval( -// fetchTodaysHimMessages, -// Temporal.Duration.from('PT5M').total('millisecond'), -// ); -// } - -export const getSingleHimMessageOfToday = ( - hid: string, -): Promise => himMessageCache.get(hid); diff --git a/src/server/HAFAS/JourneyDetails.ts b/src/server/HAFAS/JourneyDetails.ts deleted file mode 100644 index e2b74ea45..000000000 --- a/src/server/HAFAS/JourneyDetails.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { adjustProductOperator } from '@/server/HAFAS/helper/adjustProductOperator'; -import type { - AllowedHafasProfile, - HafasResponse, - ParsedCommon, -} from '@/types/HAFAS'; -import type { - JourneyDetailsRequest, - JourneyDetailsResponse, - ParsedJourneyDetails, -} from '@/types/HAFAS/JourneyDetails'; -import { parse } from 'date-fns'; -import makeRequest from './Request'; -import parseAuslastung from './helper/parseAuslastung'; -import parseMessages from './helper/parseMessages'; -import parseStop from './helper/parseStop'; - -export const parseJourneyDetails = ( - d: HafasResponse, - common: ParsedCommon, -): Promise => { - const journey = d.svcResL[0].res.journey; - const mainProduct = common.prodL[journey.prodX]; - adjustProductOperator(mainProduct, common, journey.stopL); - - const date = parse(journey.date, 'yyyyMMdd', new Date()); - if (!mainProduct.name) { - mainProduct.name = `${mainProduct.type} ${mainProduct.number}`; - } - const stops = journey.stopL.map((stop) => parseStop(stop, common, date)); - if (!stops.length) { - return Promise.resolve(undefined); - } - - const parsedJourney = { - train: mainProduct, - auslastung: parseAuslastung(journey.dTrnCmpSX, common.tcocL), - jid: journey.jid, - stops, - firstStop: stops[0], - lastStop: stops.at(-1), - messages: parseMessages(journey.msgL, common), - polylines: common.polyL, - }; - - return Promise.resolve(parsedJourney as ParsedJourneyDetails); -}; - -export const HafasJourneyDetails = async ( - jid: string, - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise => { - const req: JourneyDetailsRequest = { - req: { jid, getPolyline: true, polySplitting: true }, - meth: 'JourneyDetails', - }; - - try { - const result = await makeRequest( - req, - raw ? undefined : parseJourneyDetails, - profile, - ); - return result; - } catch { - return undefined; - } -}; diff --git a/src/server/HAFAS/JourneyMatch.ts b/src/server/HAFAS/JourneyMatch.ts deleted file mode 100644 index f7534f36d..000000000 --- a/src/server/HAFAS/JourneyMatch.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { HafasJourneyDetails } from '@/server/HAFAS/JourneyDetails'; -import { Cache, CacheDatabase } from '@/server/cache'; -import { AllowedHafasProfile } from '@/types/HAFAS'; -import type { HafasResponse, ParsedCommon } from '@/types/HAFAS'; -import type { - EnrichedJourneyMatchOptions, - JourneyMatchOptions, - JourneyMatchRequest, - JourneyMatchResponse, - ParsedJourneyMatchResponse, -} from '@/types/HAFAS/JourneyMatch'; -import { format, parse, subDays } from 'date-fns'; -import makeRequest, { HafasError } from './Request'; -import parseMessages from './helper/parseMessages'; -import parseStop from './helper/parseStop'; - -const journeyMatchCache = new Cache( - CacheDatabase.HAFASJourneyMatch, -); - -const parseJourneyMatch = ( - d: HafasResponse, - common: ParsedCommon, -): Promise => { - return Promise.all( - d.svcResL[0].res.jnyL.map((j) => { - const date = parse(j.date, 'yyyyMMdd', new Date()); - const train = common.prodL[j.prodX]; - const stops = j.stopL.map((stop) => parseStop(stop, common, date)); - - return { - train, - stops, - jid: j.jid, - firstStop: stops[0], - lastStop: stops.at(-1)!, - messages: parseMessages(j.msgL, common), - }; - }), - ); -}; - -const JourneyMatch = async ( - { - trainName, - initialDepartureDate, - withOEV, - }: Omit & { - withOEV?: boolean; - }, - profile: AllowedHafasProfile = AllowedHafasProfile.DB, - raw?: boolean, -): Promise => { - let date = initialDepartureDate; - - if (!date) { - const now = new Date(); - - date = now.getHours() < 3 ? subDays(now, 1) : now; - } - - const formattedDate = format(date, 'yyyyMMdd'); - const cacheKey = `${trainName}-${formattedDate}-${withOEV}-${profile}`; - try { - if (!raw) { - const cached = await journeyMatchCache.get(cacheKey); - if (cached) { - return cached; - } - } - const req: JourneyMatchRequest = { - req: { - date: formattedDate, - input: trainName, - jnyFltrL: withOEV - ? undefined - : [ - { - mode: 'INC', - type: 'PROD', - value: '31', - }, - ], - onlyRT: false, - }, - meth: 'JourneyMatch', - }; - - const result = await makeRequest( - req, - raw ? undefined : parseJourneyMatch, - profile, - ); - - if (!raw) { - const shorterTTL = !result.length ? 'PT2H' : undefined; - void journeyMatchCache.set(cacheKey, result, shorterTTL); - } - - return result; - } catch (e) { - if (e instanceof HafasError && e.errorCode === 'NO_MATCH') { - void journeyMatchCache.set(cacheKey, [], 'PT1H'); - } - // We just ignore errors and pretend nothing got returned. - return []; - } -}; - -export default JourneyMatch; - -const fallbackTypeRegex = /(.+?)( |\d|\b).*\d+/; - -export async function enrichedJourneyMatch( - { - withOEV, - ...options - }: Omit & { - withOEV?: boolean; - }, - profile?: AllowedHafasProfile, -): Promise { - const journeyMatches = (await JourneyMatch(options, profile)).filter( - (match) => match.train.type !== 'Flug', - ); - - const limitedJourneyMatches = options.limit - ? journeyMatches.slice(0, options.limit) - : journeyMatches; - - for (const j of limitedJourneyMatches) { - const details = await HafasJourneyDetails(j.jid, profile); - if (!details) continue; - - j.firstStop = details.firstStop; - j.lastStop = details.lastStop; - j.stops = details.stops; - // j.train = details.train; - - if (!j.train.type) { - j.train.type = fallbackTypeRegex.exec(j.train.name)?.[1]; - } - } - - return limitedJourneyMatches; -} diff --git a/src/server/HAFAS/LocMatch.ts b/src/server/HAFAS/LocMatch.ts deleted file mode 100644 index ac23ce16f..000000000 --- a/src/server/HAFAS/LocMatch.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Cache, CacheDatabase } from '@/server/cache'; -import type { - AllowedHafasProfile, - HafasResponse, - HafasStation, - ParsedCommon, -} from '@/types/HAFAS'; -import type { LocMatchRequest, LocMatchResponse } from '@/types/HAFAS/LocMatch'; -import makeRequest from './Request'; -import { parseLocL } from './helper/parseLocL'; - -const cache = new Cache(CacheDatabase.LocMatch); - -function parseFn( - d: HafasResponse, - parsedCommon: ParsedCommon, -): Promise { - const stations = d.svcResL[0].res.match.locL; - - return Promise.all( - stations - // .filter(s => s.extId) - .map((s) => parseLocL(s, parsedCommon.prodL)), - ); -} - -export const locMatch = async ( - searchTerm: string, - type: 'S' | 'ALL', - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise => { - const req: LocMatchRequest = { - req: { - input: { - loc: { - name: searchTerm, - type, - }, - field: 'S', - }, - }, - meth: 'LocMatch', - }; - - if (raw) { - return makeRequest(req, undefined, profile) as any; - } - - const cacheKey = `${profile}:${type}:${searchTerm}`; - const cached = await cache.get(cacheKey); - - if (cached) { - return cached; - } - - const result = await makeRequest(req, raw ? undefined : parseFn, profile); - - void cache.set(cacheKey, result); - - return result; -}; diff --git a/src/server/HAFAS/Request.ts b/src/server/HAFAS/Request.ts deleted file mode 100644 index 1b7e15f76..000000000 --- a/src/server/HAFAS/Request.ts +++ /dev/null @@ -1,169 +0,0 @@ -import parsePolyline from '@/server/HAFAS/helper/parsePolyline'; -import { UpstreamApiRequestMetric } from '@/server/admin'; -import { AllowedHafasProfile } from '@/types/HAFAS'; -import type { - Common, - GenericHafasRequest, - GenericRes, - HafasResponse, - ParsedCommon, - SingleHafasRequest, -} from '@/types/HAFAS'; -import type { - HimSearchRequest, - HimSearchResponse, -} from '@/types/HAFAS/HimSearch'; -import type { - JourneyDetailsRequest, - JourneyDetailsResponse, -} from '@/types/HAFAS/JourneyDetails'; -import type { - JourneyMatchRequest, - JourneyMatchResponse, -} from '@/types/HAFAS/JourneyMatch'; -import type { LocMatchRequest, LocMatchResponse } from '@/types/HAFAS/LocMatch'; -import type { - SearchOnTripRequest, - SearchOnTripResponse, -} from '@/types/HAFAS/SearchOnTrip'; -import type { - StationBoardRequest, - StationBoardResponse, -} from '@/types/HAFAS/StationBoard'; -import type { - TripSearchRequest, - TripSearchResponse, -} from '@/types/HAFAS/TripSearch'; -import { TRPCError } from '@trpc/server'; -import Axios from 'axios'; -import { parseLocL } from './helper/parseLocL'; -import parseProduct from './helper/parseProduct'; -import * as HafasProfiles from './profiles'; - -function createRequest( - req: SingleHafasRequest, - profileType: AllowedHafasProfile, -) { - const profile = HafasProfiles[profileType]; - const data: any = profile.config; - - const auth = data.auth; - - data.auth = undefined; - - data.svcReqL = [req]; - - data.auth = auth; - - const extraParam = 'secret' in profile ? profile.secret(data) : undefined; - - return { - data, - extraParam, - }; -} - -async function parseCommon(common: Common): Promise { - const prodL = common.prodL.map((p) => parseProduct(p, common)); - const locL = await Promise.all(common.locL.map((l) => parseLocL(l, prodL))); - const polyL = common.polyL?.map((p) => parsePolyline(p, locL)); - - return { - ...common, - locL, - prodL, - polyL, - }; -} - -export class HafasError extends TRPCError { - customError = true; - data: { - request: SingleHafasRequest; - response: HafasResponse; - profile: AllowedHafasProfile; - }; - errorCode: string | undefined; - constructor( - request: SingleHafasRequest, - response: HafasResponse, - profile: AllowedHafasProfile, - ) { - super({ - message: `${request.meth} HAFAS Error`, - code: 'INTERNAL_SERVER_ERROR', - }); - Error.captureStackTrace(this, HafasError); - if (response?.svcResL?.length) { - this.errorCode = response.svcResL[0].err; - this.message = response.svcResL[0].err; - } - this.data = { - request, - response, - profile, - }; - } -} - -type CommonHafasResponse< - T extends string, - R extends GenericHafasRequest, -> = R extends TripSearchRequest - ? TripSearchResponse - : R extends StationBoardRequest - ? StationBoardResponse - : R extends HimSearchRequest - ? HimSearchResponse - : R extends JourneyMatchRequest - ? JourneyMatchResponse - : R extends LocMatchRequest - ? LocMatchResponse - : R extends JourneyDetailsRequest - ? JourneyDetailsResponse - : R extends SearchOnTripRequest - ? SearchOnTripResponse - : never; -async function makeRequest< - R extends SingleHafasRequest, - HR extends GenericRes = CommonHafasResponse, - P = HR, ->( - hafasRequest: R, - parseFn: (d: HafasResponse
, pc: ParsedCommon) => Promise

= (d) => - d as any, - profile: AllowedHafasProfile = AllowedHafasProfile.DB, -): Promise

{ - const { data, extraParam } = createRequest(hafasRequest, profile); - - if (process.env.NODE_ENV === 'test') { - // biome-ignore lint/suspicious/noConsoleLog: test - console.log(JSON.stringify(hafasRequest)); - // biome-ignore lint/suspicious/noConsoleLog: test - console.log(extraParam); - } - UpstreamApiRequestMetric.inc({ - api: `hafas-${hafasRequest.meth}`, - }); - - const r = ( - await Axios.post>(HafasProfiles[profile].url, data, { - params: extraParam, - }) - ).data; - - if (('err' in r && r.err !== 'OK') || r.svcResL[0].err !== 'OK') { - throw new HafasError(hafasRequest, r, profile); - } - - const rawCommon = r.svcResL[0].res.common; - - if (!rawCommon) { - throw new HafasError(hafasRequest, r, profile); - } - const parsedCommon = await parseCommon(rawCommon); - - return parseFn(r, parsedCommon); -} - -export default makeRequest; diff --git a/src/server/HAFAS/SearchOnTrip.ts b/src/server/HAFAS/SearchOnTrip.ts deleted file mode 100644 index 7dbc98f64..000000000 --- a/src/server/HAFAS/SearchOnTrip.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { - AllowedHafasProfile, - HafasResponse, - ParsedCommon, -} from '@/types/HAFAS'; -import type { - SearchOnTripRequest, - SearchOnTripResponse, -} from '@/types/HAFAS/SearchOnTrip'; -import type { SingleRoute } from '@/types/routing'; -import makeRequest from './Request'; -import { Journey } from './TripSearch/parse'; - -const parseSearchOnTrip = ( - d: HafasResponse, - common: ParsedCommon, -) => { - const joruney = new Journey(d.svcResL[0].res.outConL[0], common); - return joruney.parseJourney(); -}; - -export default ( - req: SearchOnTripRequest['req'], - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise => { - const request: SearchOnTripRequest = { - req, - meth: 'SearchOnTrip', - }; - - return makeRequest(request, raw ? undefined : parseSearchOnTrip, profile); -}; diff --git a/src/server/HAFAS/StationBoard/StationBoardToTimetables.ts b/src/server/HAFAS/StationBoard/StationBoardToTimetables.ts deleted file mode 100644 index b86588ac2..000000000 --- a/src/server/HAFAS/StationBoard/StationBoardToTimetables.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { calculateVia } from '@/server/iris/helper'; -import type { Abfahrt } from '@/types/iris'; -import type { RouteStop } from '@/types/routing'; -import type { - ArrivalStationBoardEntry, - DepartureStationBoardEntry, -} from '@/types/stationBoard'; - -export type MappedHafasArrivals = Record< - string, - ArrivalStationBoardEntry | undefined ->; - -const stationMap = (s: RouteStop) => ({ - name: s.station.name, - cancelled: s.cancelled, - additional: s.additional, -}); - -const mapDepartureRoute = (departureRoute: RouteStop[]) => { - const mapped = departureRoute.map(stationMap); - - calculateVia(mapped.slice(1)); - - return mapped; -}; - -export default ( - j: DepartureStationBoardEntry, - hafasArrivals: MappedHafasArrivals, - idSet: Set, -): Abfahrt | undefined => { - if (!j.stops) return; - - const id = `${j.jid}${j.train.number}`; - - if (idSet.has(id)) return; - idSet.add(id); - const matchingArrival = hafasArrivals[id]; - const arrivalRoute = matchingArrival?.stops?.map(stationMap) || []; - - arrivalRoute.pop(); - const splittedName = j.stops[0].station.name.split(','); - const townSuffix = `,${splittedName.at(-1)}`; - - if (j.stops.every((s) => s.station.name.endsWith(townSuffix))) { - for (const s of j.stops) - s.station.name = s.station.name.replace(townSuffix, ''); - } - - return { - initialDeparture: j.stops[0].departure!.scheduledTime, - initialStopPlace: j.stops[0].station.evaNumber, - arrival: matchingArrival?.arrival, - departure: j.departure, - currentStopPlace: { - name: j.currentStation.name, - evaNumber: j.currentStation.evaNumber, - }, - destination: j.finalDestination, - scheduledDestination: j.finalDestination, - id, - cancelled: j.cancelled, - rawId: id, - mediumId: id, - productClass: '', - messages: { - qos: [], - delay: [], - him: - j.messages?.map((m) => ({ - text: m.txtN, - head: m.txtS || 'Information', - value: -1, - })) || [], - }, - platform: j.departure.platform ?? '', - scheduledPlatform: j.departure.scheduledPlatform ?? '', - route: [...arrivalRoute, ...mapDepartureRoute(j.stops)], - train: { - type: '', - number: '', - ...j.train, - }, - }; -}; diff --git a/src/server/HAFAS/StationBoard/index.ts b/src/server/HAFAS/StationBoard/index.ts deleted file mode 100644 index 8bca5c661..000000000 --- a/src/server/HAFAS/StationBoard/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import makeRequest from '@/server/HAFAS/Request'; -import { timezone } from '@/timezone'; -import type { AllowedHafasProfile, JourneyFilter } from '@/types/HAFAS'; -import type { StationBoardRequest } from '@/types/HAFAS/StationBoard'; -import type { - ArrivalStationBoardEntry, - DepartureStationBoardEntry, - StationBoardEntry, -} from '@/types/stationBoard'; -import { format } from 'date-fns'; -import parse from './parse'; - -interface Options { - date?: Date; - direction?: string; - station: string; - type: 'ARR' | 'DEP'; - filter?: JourneyFilter[]; -} -function stationBoard( - options: Omit & { type: 'ARR' }, - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise; -function stationBoard( - options: Omit & { type: 'DEP' }, - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise; -function stationBoard( - { station, date = new Date(), type, direction, filter }: Options, - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise { - const req: StationBoardRequest = { - req: { - type, - getPasslist: true, - maxJny: 250, - jnyFltrL: filter, - date: format(date, 'yyyyMMdd', { - in: timezone.europeBerlin, - }), - time: format(date, 'HHmmss', { - in: timezone.europeBerlin, - }), - stbLoc: { - lid: `A=1@L=${station}`, - }, - dirLoc: direction - ? { - lid: `A=1@L=${direction}`, - } - : undefined, - }, - meth: 'StationBoard', - }; - - return makeRequest(req, raw ? undefined : parse, profile); -} -export default stationBoard; diff --git a/src/server/HAFAS/StationBoard/parse.ts b/src/server/HAFAS/StationBoard/parse.ts deleted file mode 100644 index 2c16b45aa..000000000 --- a/src/server/HAFAS/StationBoard/parse.ts +++ /dev/null @@ -1,63 +0,0 @@ -import parseCommonArrival from '@/server/HAFAS/helper/parseCommonArrival'; -import parseCommonDeparture from '@/server/HAFAS/helper/parseCommonDeparture'; -import parseMessages from '@/server/HAFAS/helper/parseMessages'; -import parseStop from '@/server/HAFAS/helper/parseStop'; -import type { - CommonArrival, - CommonDeparture, - HafasResponse, - ParsedCommon, -} from '@/types/HAFAS'; -import type { - StationBoardJny, - StationBoardResponse, -} from '@/types/HAFAS/StationBoard'; -import type { StationBoardEntry } from '@/types/stationBoard'; -import { parse } from 'date-fns'; - -const isArrival = (a: CommonArrival | CommonDeparture): a is CommonArrival => - 'aOutR' in a; - -const parseStationBoardResponse = ( - jny: StationBoardJny, - common: ParsedCommon, -): StationBoardEntry => { - const date = parse(jny.date, 'yyyyMMdd', new Date()); - const product = common.prodL[jny.prodX]; - const commonResponse = { - train: product || {}, - finalDestination: jny.dirTxt, - jid: jny.jid, - cancelled: jny.isCncl, - currentStation: common.locL[jny.stbStop.locX], - stops: jny.stopL?.map((s) => parseStop(s, common, date)), - messages: parseMessages(jny.msgL, common), - }; - - if (!commonResponse.finalDestination && commonResponse.stops) { - commonResponse.finalDestination = commonResponse.stops.at(-1)!.station.name; - } - - if (isArrival(jny.stbStop)) { - return { - ...commonResponse, - arrival: parseCommonArrival(jny.stbStop, date, common), - }; - } - - return { - ...commonResponse, - departure: parseCommonDeparture(jny.stbStop, date, common), - }; -}; - -export default ( - r: HafasResponse, - parsedCommon: ParsedCommon, -): Promise => { - const abfahrten: StationBoardEntry[] = r.svcResL[0].res.jnyL.map((j) => - parseStationBoardResponse(j, parsedCommon), - ); - - return Promise.resolve(abfahrten); -}; diff --git a/src/server/HAFAS/TripSearch/NetzcardBetreiber.json b/src/server/HAFAS/TripSearch/NetzcardBetreiber.json deleted file mode 100644 index da2be607b..000000000 --- a/src/server/HAFAS/TripSearch/NetzcardBetreiber.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - "DB Fernverkehr Bus", - "DB Fernverkehr AG", - "DB Regio AG", - "DB Regio AG Baden-Württemberg", - "DB Regio AG Bayern", - "DB Regio AG Mitte", - "DB Regio AG Nord", - "DB Regio AG Nordost", - "DB Regio AG NRW", - "DB Arriva", - "Sylt Shuttle Plus", - "DB Regio AG Südost", - "DB Regio AG Südost DEKT 2017", - "DB Regio Bus Mitte", - "Autokraft", - "Dithmarschenbus (DB Regio Bus Nord GmbH)", - "DB RegioNetz Verkehrs GmbH Erzgebirgsbahn", - "DB RegioNetz Verkehrs GmbH Gäubodenbahn", - "DB RegioNetz Verkehrs GmbH Kurhessenbahn", - "DB RegioNetz Verkehrs GmbH Oberweißbacher Berg+Schwarzatalbahn", - "DB Fernverkehr AG", - "DB Regio AG S-Bahn München", - "DB Regio AG S-Bahn Rhein-Main", - "DB Regio AG S-Bahn Stuttgart", - "S-Bahn Berlin", - "S-Bahn Hamburg", - "DB RegioNetz Verkehrs GmbH Südostbayernbahn", - "DB Regio AG Mitte SÜWEX", - "DB RegioNetz Verkehrs GmbH Westfrankenbahn" -] diff --git a/src/server/HAFAS/TripSearch/TripSearch.ts b/src/server/HAFAS/TripSearch/TripSearch.ts deleted file mode 100644 index 3e3efd5fa..000000000 --- a/src/server/HAFAS/TripSearch/TripSearch.ts +++ /dev/null @@ -1,185 +0,0 @@ -import makeRequest from '@/server/HAFAS/Request'; -import mapLoyalityCard from '@/server/HAFAS/TripSearch/mapLoyalityCard'; -import { timezone } from '@/timezone'; -import type { - AllowedHafasProfile, - JourneyFilter, - OptionalLocL, -} from '@/types/HAFAS'; -import type { - TripSearchOptionsV3, - TripSearchOptionsV4, - TripSearchRequest, -} from '@/types/HAFAS/TripSearch'; -import type { RoutingResult } from '@/types/routing'; -import { format } from 'date-fns'; -import NetzcardBetreiber from './NetzcardBetreiber.json'; -import tripSearchParse from './parse'; - -const netzcardFilter: JourneyFilter[] = NetzcardBetreiber.map((betreiber) => ({ - mode: 'INC', - type: 'OP', - value: betreiber, -})); - -const bc100Filter: JourneyFilter[] = [ - { - mode: 'EXC', - type: 'ATTRJ', - value: 'DU', - }, -]; - -const onlyRegionalFilter: JourneyFilter[] = [ - { - value: '1016', - mode: 'INC', - type: 'PROD', - }, -]; - -const profileConfig = { - db: { - cfg: { - rtMode: 'HYBRID', - }, - }, -}; - -function convertSingleCoordinate(singleCoordinate: number): number { - const splittedCoordinate = singleCoordinate.toString().split('.'); - const pre = splittedCoordinate[0].padStart(2, '0'); - const post = (splittedCoordinate[1] || '').padEnd(6, '0').slice(0, 6); - - return Number.parseInt(`${pre}${post}`); -} - -function startDestinationMap( - startDest: string | TripSearchOptionsV4['start'], -): OptionalLocL { - let eva: string | undefined; - if (typeof startDest === 'string') { - eva = startDest; - } else { - if (startDest.type === 'coordinate') { - return { - crd: { - x: convertSingleCoordinate(startDest.longitude), - y: convertSingleCoordinate(startDest.latitude), - }, - }; - } - eva = startDest.evaNumber; - } - - return { - lid: `A=1@L=${eva}@B=1`, - }; -} - -export function tripSearch( - { - start, - destination, - via, - time, - transferTime = -1, - maxChanges = -1, - searchForDeparture = true, - getPasslist = true, - economic = true, - ushrp = false, - getPolyline = false, - getIV = true, - numF = 6, - ctxScr, - onlyRegional, - onlyNetzcard, - onlyBC100, - tarif, - }: TripSearchOptionsV3 | TripSearchOptionsV4, - profile?: AllowedHafasProfile, - raw?: boolean, -): Promise { - let requestTypeSpecific: - | { outDate: string; outTime: string } - | { - ctxScr: string; - }; - if (!time && !ctxScr) { - time = new Date(); - } - - if (time) { - requestTypeSpecific = { - outDate: format(time, 'yyyyMMdd', { - in: timezone.europeBerlin, - }), - outTime: format(time, 'HHmmss', { - in: timezone.europeBerlin, - }), - }; - } else if (ctxScr) { - requestTypeSpecific = { - ctxScr, - }; - } else { - throw new Error('Either Time or Context required'); - } - - const journeyFilter: JourneyFilter[] = []; - if (onlyRegional) { - journeyFilter.push(...onlyRegionalFilter); - } - if (onlyNetzcard) { - journeyFilter.push(...netzcardFilter); - } - if (onlyBC100) { - journeyFilter.push(...bc100Filter); - } - const arrLoc = startDestinationMap(destination); - const depLoc = startDestinationMap(start); - - const req: TripSearchRequest = { - req: { - jnyFltrL: journeyFilter.length ? journeyFilter : undefined, - // getPT: true, - numF, - ...requestTypeSpecific, - maxChg: maxChanges === -1 ? undefined : maxChanges, - minChgTime: transferTime || undefined, - getPasslist, - economic, - ushrp, - getPolyline, - getIV, - // arrival / departure - outFrwd: searchForDeparture ? undefined : false, - arrLocL: [arrLoc], - depLocL: [depLoc], - viaLocL: via?.length - ? via.map((via) => ({ - loc: { - lid: `A=1@L=${'evaNumber' in via ? via.evaNumber : via.evaId}`, - }, - min: via.minChangeTime, - })) - : undefined, - trfReq: tarif - ? { - jnyCl: tarif.class, - cType: 'PK', - tvlrProf: tarif.traveler.map((t) => ({ - type: t.type, - redtnCard: mapLoyalityCard(t.loyalityCard, profile), - })), - } - : undefined, - }, - meth: 'TripSearch', - // @ts-expect-error spread works - ...profileConfig[profile ?? 'db'], - }; - - return makeRequest(req, raw ? undefined : tripSearchParse, profile); -} diff --git a/src/server/HAFAS/TripSearch/mapLoyalityCard.ts b/src/server/HAFAS/TripSearch/mapLoyalityCard.ts deleted file mode 100644 index 3114f22d1..000000000 --- a/src/server/HAFAS/TripSearch/mapLoyalityCard.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { AllowedHafasProfile } from '@/types/HAFAS'; -import type { LoyalityCard } from '@/types/HAFAS/Tarif'; - -const DBLoyalityCards: { [key in LoyalityCard]: number } = { - BC25First: 1, - BC25Second: 2, - BC50First: 3, - BC50Second: 4, - SHCard: 14, - ATVorteilscard: 9, - CHGeneral: 15, - CHHalfWithRailplus: 10, - CHHalfWithoutRailplus: 11, - NLWithRailplus: 12, - NLWithoutRailplus: 13, - BC100First: 16, - BC100Second: 17, -}; - -// TODO: Expand for other profiles, DB only for now -export default ( - loyalityCard?: LoyalityCard, - _profile: AllowedHafasProfile = AllowedHafasProfile.DB, -): number | undefined => { - if (!loyalityCard) return; - - return DBLoyalityCards[loyalityCard]; -}; diff --git a/src/server/HAFAS/TripSearch/mergeSegments.ts b/src/server/HAFAS/TripSearch/mergeSegments.ts deleted file mode 100644 index cf4cdd5d7..000000000 --- a/src/server/HAFAS/TripSearch/mergeSegments.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { RouteJourneySegment } from '@/types/routing'; - -export default (segments: RouteJourneySegment[]): RouteJourneySegment[] => { - const mergedSegments: RouteJourneySegment[] = []; - let currentSegment = segments.shift(); - - while (currentSegment) { - const nextSegment = segments.shift(); - - if (currentSegment.type !== 'WALK' || !nextSegment) { - mergedSegments.push(currentSegment); - currentSegment = nextSegment; - } else if (nextSegment.type === 'WALK') { - currentSegment.arrival = nextSegment.arrival; - currentSegment.duration += nextSegment.duration; - currentSegment.segmentDestination = nextSegment.segmentDestination; - mergedSegments.push(currentSegment); - currentSegment = segments.shift(); - } else { - mergedSegments.push(currentSegment, nextSegment); - currentSegment = segments.shift(); - } - } - - return mergedSegments; -}; diff --git a/src/server/HAFAS/TripSearch/parse.ts b/src/server/HAFAS/TripSearch/parse.ts deleted file mode 100644 index 1fdb5ab29..000000000 --- a/src/server/HAFAS/TripSearch/parse.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { TransportType } from '@/external/types'; -import mergeSegments from '@/server/HAFAS/TripSearch/mergeSegments'; -import { adjustProductOperator } from '@/server/HAFAS/helper/adjustProductOperator'; -import parseAuslastung from '@/server/HAFAS/helper/parseAuslastung'; -import parseCommonArrival from '@/server/HAFAS/helper/parseCommonArrival'; -import parseCommonDeparture from '@/server/HAFAS/helper/parseCommonDeparture'; -import parseDuration from '@/server/HAFAS/helper/parseDuration'; -import parseMessages from '@/server/HAFAS/helper/parseMessages'; -import parseStop from '@/server/HAFAS/helper/parseStop'; -import parseTarif from '@/server/HAFAS/helper/parseTarif'; -import { getGroups } from '@/server/StopPlace/search'; -import type { - CommonStop, - CommonStopInfo, - HafasResponse, - ParsedCommon, -} from '@/types/HAFAS'; -import type { - Jny, - OutConL, - SecL, - TripSearchResponse, -} from '@/types/HAFAS/TripSearch'; -import type { - RouteJourney, - RouteJourneySegment, - RouteStop, - RoutingResult, - SingleRoute, -} from '@/types/routing'; -import type { MinimalStopPlace } from '@/types/stopPlace'; -import { differenceInMilliseconds, parse } from 'date-fns'; - -const nameRegex = /O=([^@]+)/; -const evaRegex = /L=(\d+)/; - -async function isSameSalesGroup(eva1: string, eva2: string) { - const [groupsOf1, groupsOf2] = await Promise.all([ - getGroups(eva1), - getGroups(eva2), - ]); - return groupsOf1.STATION?.includes(eva2) && groupsOf2.STATION?.includes(eva1); -} - -function parseFullStation(fullStation: string): MinimalStopPlace { - const titleMatch = nameRegex.exec(fullStation); - const idMatch = evaRegex.exec(fullStation); - - let name = ''; - let evaNumber = ''; - - if (titleMatch?.[1]) name = titleMatch[1]; - if (idMatch?.[1]) evaNumber = idMatch[1].padStart(7, '0'); - - return { - name, - evaNumber, - }; -} - -function adjustToFirstTrain( - departure: CommonStopInfo, - segments: RouteJourneySegment[], -) { - if (segments.length && segments[0].type !== 'JNY') { - const firstTrainSegment = segments.find((s) => s.type === 'JNY'); - - if (firstTrainSegment) { - departure.delay = firstTrainSegment.arrival.delay; - } - } -} - -function adjustToLastTrain( - arrival: CommonStopInfo, - segments: RouteJourneySegment[], -) { - if (segments.length && segments.at(-1)?.type !== 'JNY') { - const allTrainSegments = segments.filter((s) => s.type === 'JNY'); - - if (allTrainSegments.length) { - const lastTrainSegment = allTrainSegments.at(-1)!; - - arrival.delay = lastTrainSegment.arrival.delay; - } - } -} - -const AllowedLegTypes = new Set(['JNY', 'WALK', 'TRSF']); - -export class Journey { - private date: Date; - constructor( - private raw: OutConL, - private common: ParsedCommon, - ) { - this.date = parse(raw.date, 'yyyyMMdd', new Date()); - } - parseJourney = async (): Promise => { - const allSegments = ( - await Promise.all( - this.raw.secL - .filter((leg) => AllowedLegTypes.has(leg.type)) - .map(this.parseSegment), - ) - ).filter(Boolean); - - const segments = mergeSegments(allSegments); - - const arrival = parseCommonArrival(this.raw.arr, this.date, this.common); - const departure = parseCommonDeparture( - this.raw.dep, - this.date, - this.common, - ); - - adjustToFirstTrain(departure, segments); - adjustToLastTrain(arrival, segments); - - return Promise.resolve({ - id: this.raw.cksum, - cid: this.raw.cid, - date: this.date, - duration: parseDuration(this.raw.dur), - changes: this.raw.chg, - isRideable: !this.raw.isNotRdbl, - arrival, - departure, - segments, - segmentTypes: segments - .map((s) => (s.type === 'JNY' ? s.train.type : s.train.name)) - .filter(Boolean as any), - tarifSet: parseTarif(this.raw.trfRes), - }); - }; - private parseStops = (stops: CommonStop[] | undefined): RouteStop[] => { - if (!stops) return []; - - return stops.map((stop) => parseStop(stop, this.common, this.date)); - }; - private parseSegmentJourney = (jny: Jny): RouteJourney => { - const [, fullStart, fullDestination, , , , , , ,] = jny.ctxRecon.split('$'); - const product = this.common.prodL[jny.prodX]; - adjustProductOperator(product, this.common, jny.stopL); - - return { - train: product, - cancelled: jny.isCncl, - changeDuration: jny.chgDurR, - segmentStart: parseFullStation(fullStart), - segmentDestination: parseFullStation(fullDestination), - stops: this.parseStops(jny.stopL), - finalDestination: jny.dirTxt, - jid: jny.jid, - auslastung: parseAuslastung(jny.dTrnCmpSX, this.common.tcocL), - messages: parseMessages(jny.msgL, this.common), - }; - }; - private parseSegment = async ( - t: SecL, - ): Promise => { - switch (t.type) { - case 'JNY': { - const arrival = parseCommonArrival(t.arr, this.date, this.common); - const departure = parseCommonDeparture(t.dep, this.date, this.common); - - return { - arrival, - departure, - duration: - arrival.scheduledTime && - departure.scheduledTime && - differenceInMilliseconds( - arrival.scheduledTime, - departure.scheduledTime, - ), - wings: t.parJnyL - ? t.parJnyL.map(this.parseSegmentJourney) - : undefined, - // reservationStatus: t.resState, - // reservationRecommandation: t.resRecommendation, - // icoX: this.common.icoL[t.icoX], - ...this.parseSegmentJourney(t.jny), - type: 'JNY', - }; - } - case 'TRSF': - case 'WALK': { - const segmentStart = this.common.locL[t.dep.locX]; - const segmentDestination = this.common.locL[t.arr.locX]; - - // same sales group = same Station - if ( - await isSameSalesGroup( - segmentStart.evaNumber, - segmentDestination.evaNumber, - ) - ) { - return; - } - - return { - type: 'WALK', - train: { - name: 'Fußweg', - type: 'Fußweg', - transportType: TransportType.Walk, - }, - arrival: parseCommonArrival(t.arr, this.date, this.common), - departure: parseCommonDeparture(t.dep, this.date, this.common), - duration: parseDuration(t.gis.durS), - segmentStart: this.common.locL[t.dep.locX], - segmentDestination: this.common.locL[t.arr.locX], - }; - } - default: { - return undefined; - } - } - }; -} - -export default async ( - r: HafasResponse, - parsedCommon: ParsedCommon, -): Promise => { - const routes = ( - await Promise.all( - r.svcResL[0].res.outConL.flatMap((j) => - new Journey(j, parsedCommon).parseJourney(), - ), - ) - ).filter((j) => j.segments.length); - - return { - context: { - earlier: r.svcResL[0].res.outCtxScrB, - later: r.svcResL[0].res.outCtxScrF, - }, - routes, - }; -}; diff --git a/src/server/HAFAS/helper/__tests__/createCtxRecon.test.ts b/src/server/HAFAS/helper/__tests__/createCtxRecon.test.ts deleted file mode 100644 index 770cb729d..000000000 --- a/src/server/HAFAS/helper/__tests__/createCtxRecon.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import createCtxRecon from '@/server/HAFAS/helper/createCtxRecon'; -import type { - RouteValidArrivalStop, - RouteValidDepartureStop, -} from '@/types/HAFAS/JourneyDetails'; -import { addSeconds, parse } from 'date-fns'; -import { describe, expect, it } from 'vitest'; - -describe('createCtxRecon', () => { - const expectedDeparture = '201912131045'; - const expectedArrival = '201912131220'; - const departureTime = parse(expectedDeparture, 'yyyyMMddHHmm', 0); - const arrivalTime = parse(expectedArrival, 'yyyyMMddHHmm', 0); - - const firstStop: RouteValidDepartureStop = { - station: { - evaNumber: '123', - name: 'dummy', - }, - departure: { - scheduledTime: departureTime, - time: departureTime, - }, - }; - - const lastStop: RouteValidArrivalStop = { - station: { - evaNumber: '321', - name: 'otherDummy', - }, - arrival: { - scheduledTime: arrivalTime, - time: addSeconds(arrivalTime, 123), - }, - }; - - it('Default replacementNumber is one', () => { - expect( - createCtxRecon({ - firstStop, - lastStop, - trainName: 'test', - }), - ).toBe( - `¶HKI¶T$A=1@L=${firstStop.station.evaNumber}@a=128@$A=1@L=${lastStop.station.evaNumber}@a=128@$${expectedDeparture}$${expectedArrival}$test$$1$`, - ); - }); - - it('Ersatzfahrt has replacement 2', () => { - expect( - createCtxRecon({ - firstStop, - lastStop, - trainName: 'ersatz', - messages: [ - { - type: '', - code: '', - icoX: 0, - txtN: 'Ersatzfahrt', - }, - ], - }), - ).toBe( - `¶HKI¶T$A=1@L=${firstStop.station.evaNumber}@a=128@$A=1@L=${lastStop.station.evaNumber}@a=128@$${expectedDeparture}$${expectedArrival}$ersatz$$2$`, - ); - }); -}); diff --git a/src/server/HAFAS/helper/adjustProductOperator.ts b/src/server/HAFAS/helper/adjustProductOperator.ts deleted file mode 100644 index f2abf18eb..000000000 --- a/src/server/HAFAS/helper/adjustProductOperator.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { - CommonProductInfo, - CommonStop, - ParsedCommon, -} from '@/types/HAFAS'; - -export const adjustProductOperator = ( - mainProduct: CommonProductInfo, - common: ParsedCommon, - stops?: CommonStop[], -): void => { - if (!stops) return; - const relevantProdX = new Set( - stops.flatMap((s) => [s.aProdX, s.dProdX]).filter(Boolean), - ); - const operatorNames = new Set(); - if (mainProduct.operator?.name) { - operatorNames.add(mainProduct.operator.name); - } - for (const prodX of relevantProdX) { - const operatorName = common.prodL[prodX].operator?.name; - if (operatorName) { - operatorNames.add(operatorName); - } - } - if (operatorNames.size && mainProduct.operator) { - mainProduct.operator.name = [...operatorNames].join(', '); - } -}; diff --git a/src/server/HAFAS/helper/checkCoachSequence.ts b/src/server/HAFAS/helper/checkCoachSequence.ts deleted file mode 100644 index 818a19ef7..000000000 --- a/src/server/HAFAS/helper/checkCoachSequence.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ParsedProduct, TrnCmpSX } from '@/types/HAFAS'; -import { addDays, isBefore } from 'date-fns'; - -const allowdTypes = new Set(['ICE', 'IC', 'TGV', 'EC', 'ECE', 'RJ', 'D']); - -export default ( - scheduledTime: Date, - trnCmpSX?: TrnCmpSX, - train?: ParsedProduct, -): boolean | undefined => { - if (isBefore(addDays(new Date(), 1), scheduledTime)) return false; - if (train?.type && allowdTypes.has(train.type)) return true; - - if (trnCmpSX?.tcM) return true; -}; diff --git a/src/server/HAFAS/helper/createCtxRecon.ts b/src/server/HAFAS/helper/createCtxRecon.ts deleted file mode 100644 index 119766685..000000000 --- a/src/server/HAFAS/helper/createCtxRecon.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { RemL } from '@/types/HAFAS'; -import type { - RouteValidArrivalStop, - RouteValidDepartureStop, -} from '@/types/HAFAS/JourneyDetails'; -import { format } from 'date-fns'; - -interface CreateCtxReconOptions { - firstStop: RouteValidDepartureStop; - lastStop: RouteValidArrivalStop; - trainName: string; - messages?: RemL[]; -} -export default ({ - firstStop, - lastStop, - trainName, - messages, -}: CreateCtxReconOptions): string => { - // highly unknown what this exactly does - // some replacement trains need a 2 here. - let replacementNumber = 1; - - if (messages?.some((m) => m.txtN.includes('Ersatzfahrt'))) { - replacementNumber = 2; - } - - return `¶HKI¶T$A=1@L=${firstStop.station.evaNumber}@a=128@$A=1@L=${ - lastStop.station.evaNumber - }@a=128@$${format( - firstStop.departure.scheduledTime, - 'yyyyMMddHHmm', - )}$${format( - lastStop.arrival.scheduledTime, - 'yyyyMMddHHmm', - )}$${trainName}$$${replacementNumber}$`; -}; diff --git a/src/server/HAFAS/helper/parseAuslastung.ts b/src/server/HAFAS/helper/parseAuslastung.ts deleted file mode 100644 index 1ae5cc241..000000000 --- a/src/server/HAFAS/helper/parseAuslastung.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { TcocL, TrnCmpSX } from '@/types/HAFAS'; - -export default ( - dTrnCmpSX?: TrnCmpSX, - tcocL?: TcocL[], -): - | { - first?: number; - second?: number; - } - | undefined => { - if (!tcocL || !dTrnCmpSX?.tcocX) return; - - const auslastung: { - first?: number; - second?: number; - } = {}; - - for (const i of dTrnCmpSX.tcocX) { - const a = tcocL[i]; - - switch (a.c) { - case 'FIRST': { - auslastung.first = a.r; - break; - } - case 'SECOND': { - auslastung.second = a.r; - break; - } - default: { - break; - } - } - } - - return auslastung; -}; diff --git a/src/server/HAFAS/helper/parseCommonArrival.ts b/src/server/HAFAS/helper/parseCommonArrival.ts deleted file mode 100644 index 59d36d42c..000000000 --- a/src/server/HAFAS/helper/parseCommonArrival.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { - CommonArrival, - CommonStopInfo, - ParsedCommon, -} from '@/types/HAFAS'; -import { differenceInMinutes } from 'date-fns'; -import parseTime from './parseTime'; - -export default ( - a: CommonArrival, - date: Date, - _common: ParsedCommon, -): CommonStopInfo => { - const scheduledTime = parseTime(date, a.aTimeS, a.aTZOffset); - let time = scheduledTime; - let delay: number | undefined; - - if (a.aTimeR) { - time = parseTime(date, a.aTimeR, a.aTZOffset); - delay = time && scheduledTime && differenceInMinutes(time, scheduledTime); - } - - return { - scheduledPlatform: a.aPlatfR && a.aPlatfS, - platform: a.aPlatfR || a.aPlatfS, - scheduledTime, - time, - delay, - cancelled: a.aCncl, - }; -}; diff --git a/src/server/HAFAS/helper/parseCommonDeparture.ts b/src/server/HAFAS/helper/parseCommonDeparture.ts deleted file mode 100644 index c542a8468..000000000 --- a/src/server/HAFAS/helper/parseCommonDeparture.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { - CommonDeparture, - CommonStopInfo, - ParsedCommon, -} from '@/types/HAFAS'; -import { differenceInMinutes } from 'date-fns'; -import parseTime from './parseTime'; - -export default ( - d: CommonDeparture, - date: Date, - _common: ParsedCommon, -): CommonStopInfo => { - const scheduledTime = parseTime(date, d.dTimeS, d.dTZOffset); - - let time = scheduledTime; - let delay: number | undefined; - - if (d.dTimeR) { - time = parseTime(date, d.dTimeR, d.dTZOffset); - delay = - time && - scheduledTime && - differenceInMinutes(time, scheduledTime, { - roundingMethod: 'floor', - }); - } - - return { - scheduledPlatform: d.dPlatfR && d.dPlatfS, - platform: d.dPlatfR || d.dPlatfS, - scheduledTime, - time, - delay, - cancelled: d.dCncl, - // messages: d.msgL ? parseMessages(d.msgL, common) : undefined, - }; -}; diff --git a/src/server/HAFAS/helper/parseDuration.ts b/src/server/HAFAS/helper/parseDuration.ts deleted file mode 100644 index f388e12c9..000000000 --- a/src/server/HAFAS/helper/parseDuration.ts +++ /dev/null @@ -1,13 +0,0 @@ -export default (duration: string | undefined | null): number => { - if (!duration) return 0; - const sanitized = duration.padStart(8, '0'); - - const days = Number.parseInt(sanitized.slice(0, 2), 10); - const hours = Number.parseInt(sanitized.slice(2, 4), 10); - const minutes = Number.parseInt(sanitized.slice(4, 6), 10); - const seconds = Number.parseInt(sanitized.slice(6, 8), 10); - - return ( - (seconds + minutes * 60 + hours * 60 * 60 + days * 60 * 60 * 24) * 1000 - ); -}; diff --git a/src/server/HAFAS/helper/parseLocL.ts b/src/server/HAFAS/helper/parseLocL.ts deleted file mode 100644 index 4be1e45ee..000000000 --- a/src/server/HAFAS/helper/parseLocL.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getIdentifiers } from '@/server/StopPlace/search'; -import type { Crd, HafasStation, LocL, ParsedProduct } from '@/types/HAFAS'; - -function normalizeStopPlaceName(name: string) { - return name.replace('(', ' (').replace(')', ') ').trim(); -} - -export function parseCoordinates(crd: Crd): { - lng: number; - lat: number; -} { - return { - lng: crd.x / 1000000, - lat: crd.y / 1000000, - }; -} - -export const parseLocL = async ( - locL: LocL, - products: ParsedProduct[], -): Promise => { - const identifiers = await getIdentifiers(locL.extId, true); - return { - evaNumber: locL.extId, - name: normalizeStopPlaceName(locL.name), - coordinates: locL.crd && parseCoordinates(locL.crd), - products: locL.pRefL?.map((p) => products[p]), - ril100: identifiers?.ril100, - }; -}; diff --git a/src/server/HAFAS/helper/parseMessages.ts b/src/server/HAFAS/helper/parseMessages.ts deleted file mode 100644 index 54f9482b3..000000000 --- a/src/server/HAFAS/helper/parseMessages.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { uniqBy } from '@/client/util'; -import type { MsgL, ParsedCommon, RemL } from '@/types/HAFAS'; - -export default ( - msgL: undefined | MsgL[], - common: ParsedCommon, -): undefined | RemL[] => { - if (!msgL) return undefined; - - const messages = uniqBy( - msgL - .map((msg) => { - const rem = common.remL[msg.remX]; - - return rem; - }) - .filter((msg) => msg && msg.txtN !== 'Gleiswechsel'), - 'txtN', - ); - - return messages; -}; diff --git a/src/server/HAFAS/helper/parsePolyline.ts b/src/server/HAFAS/helper/parsePolyline.ts deleted file mode 100644 index d125d4392..000000000 --- a/src/server/HAFAS/helper/parsePolyline.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { HafasStation, ParsedPolyline, PolyL } from '@/types/HAFAS'; -import googlePolyline from 'google-polyline'; -const { decode } = googlePolyline; - -export default (polyline: PolyL, locL: HafasStation[]): ParsedPolyline => { - return { - points: decode(polyline.crdEncYX)?.map((x) => [x[1], x[0]]), - delta: polyline.delta, - locations: polyline.ppLocRefL.map((l) => locL[l.locX]), - }; -}; diff --git a/src/server/HAFAS/helper/parseProduct.ts b/src/server/HAFAS/helper/parseProduct.ts deleted file mode 100644 index cbd1feb28..000000000 --- a/src/server/HAFAS/helper/parseProduct.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { TransportType } from '@/external/types'; -import { getLineFromNumber } from '@/server/journeys/lineNumberMapping'; -import type { Common, ParsedProduct, ProdL } from '@/types/HAFAS'; - -function mapTransportType(catCode?: string, catIn?: string): TransportType { - // SEV is categorized wrong - if (catIn === 'BSV' || catIn === 'Bsv') { - return TransportType.Bus; - } - // Flights are categorized wrong (There is no HAFAS flight catCode) - if (catIn === 'AIR') { - return TransportType.Flight; - } - switch (catCode) { - case '0': { - return TransportType.HighSpeedTrain; - } - case '1': { - return TransportType.IntercityTrain; - } - case '2': { - return TransportType.InterRegionalTrain; - } - case '3': { - return TransportType.RegionalTrain; - } - case '4': { - return TransportType.CityTrain; - } - case '5': { - return TransportType.Bus; - } - case '6': { - return TransportType.Ferry; - } - case '7': { - return TransportType.Subway; - } - case '8': { - return TransportType.Tram; - } - case '9': { - return TransportType.Shuttle; - } - default: { - return TransportType.Unknown; - } - } -} - -export default (product: ProdL, common: Common): ParsedProduct => { - const operator = - product.oprX === undefined ? undefined : common.opL[product.oprX]; - const number = product.prodCtx?.num ?? product.number; - - return { - name: product.addName || product.name, - line: - product.prodCtx?.line || - product.prodCtx?.lineId || - product.prodCtx?.matchId || - product.matchId || - product.nameS || - getLineFromNumber(number), - admin: product.prodCtx?.admin?.replaceAll('_', ''), - number, - type: - product.prodCtx && (product.prodCtx.catOut || product.prodCtx.catOutL), - transportType: mapTransportType(product.prodCtx?.catCode), - operator, - }; -}; diff --git a/src/server/HAFAS/helper/parseStop.ts b/src/server/HAFAS/helper/parseStop.ts deleted file mode 100644 index c34ea95ae..000000000 --- a/src/server/HAFAS/helper/parseStop.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { CommonStop, ParsedCommon } from '@/types/HAFAS'; -import type { RouteStop } from '@/types/routing'; -import parseAuslastung from './parseAuslastung'; -import parseCommonArrival from './parseCommonArrival'; -import parseCommonDeparture from './parseCommonDeparture'; -import parseMessages from './parseMessages'; - -export default ( - stop: CommonStop, - common: ParsedCommon, - date: Date, -): RouteStop => { - const arrival = stop.aTimeS - ? parseCommonArrival(stop, date, common) - : undefined; - const departure = stop.dTimeS - ? parseCommonDeparture(stop, date, common) - : undefined; - - return { - station: common.locL[stop.locX], - arrival, - departure, - auslastung: parseAuslastung(stop.dTrnCmpSX, common.tcocL), - additional: stop.isAdd, - cancelled: - (arrival || departure) && - (!arrival || stop.aCncl) && - (!departure || stop.dCncl), - messages: parseMessages(stop.msgL, common), - }; -}; diff --git a/src/server/HAFAS/helper/parseTarif.ts b/src/server/HAFAS/helper/parseTarif.ts deleted file mode 100644 index bd3830cee..000000000 --- a/src/server/HAFAS/helper/parseTarif.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { - HafasTarifResponse, - TarifFare, - TarifFareSet, -} from '@/types/HAFAS/TripSearch'; -import type { RouteTarifFare, RouteTarifFareSet } from '@/types/routing'; - -const parseFare = (fare: TarifFare): RouteTarifFare => { - return { - price: fare.prc, - moreExpensiveAvailable: fare.isFromPrice, - bookable: fare.isBookable, - upsell: fare.isUpsell, - targetContext: fare.targetCtx, - }; -}; - -const parseFareSet = (fareSet: TarifFareSet): RouteTarifFareSet => { - return { - fares: fareSet.fareL.map(parseFare), - }; -}; - -export default ( - tarifResponse?: HafasTarifResponse, -): RouteTarifFareSet[] | undefined => { - if (tarifResponse?.statusCode !== 'OK') return; - - return tarifResponse.fareSetL.map(parseFareSet); -}; diff --git a/src/server/HAFAS/helper/parseTime.ts b/src/server/HAFAS/helper/parseTime.ts deleted file mode 100644 index bd62e9477..000000000 --- a/src/server/HAFAS/helper/parseTime.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { addMilliseconds, addMinutes } from 'date-fns'; -import parseDuration from './parseDuration'; - -function parseTime(date: Date, time: string, tzOffset?: number): Date; -function parseTime(date: Date, time: undefined, tzOffset?: number): undefined; -function parseTime( - date: Date, - time?: string, - tzOffset?: number, -): Date | undefined { - if (time) { - let parsedDate = addMilliseconds(date, parseDuration(time)); - // Summer/Winter Time - const tzDifference = - parsedDate.getTimezoneOffset() - date.getTimezoneOffset(); - - if (tzDifference !== 0) { - parsedDate = addMinutes(parsedDate, tzDifference); - } - - if (tzOffset) { - const parsedDateTZOffset = -1 * parsedDate.getTimezoneOffset(); - if (parsedDateTZOffset !== tzOffset) { - const difference = parsedDateTZOffset - tzOffset; - parsedDate = addMinutes(parsedDate, difference); - } - } - - return parsedDate; - } -} - -export default parseTime; diff --git a/src/server/HAFAS/occupancy.ts b/src/server/HAFAS/occupancy.ts deleted file mode 100644 index 1e48e268a..000000000 --- a/src/server/HAFAS/occupancy.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { searchStopPlace } from '@/server/StopPlace/search'; -import { Cache, CacheDatabase } from '@/server/cache'; -import type { RouteAuslastung, SingleRoute } from '@/types/routing'; -import { tripSearch } from './TripSearch/TripSearch'; - -async function getRelevantTrip( - start: string, - destination: string, - trainNumber: string, - time: Date, -): Promise { - const startStations = await searchStopPlace(start, 1); - const destStations = await searchStopPlace(destination, 1); - - const startStation = startStations[0]; - const destStation = destStations[0]; - - if (!startStation) { - throw { - message: 'Start Station unknown', - data: start, - }; - } - if (!destStation) { - throw { - message: 'Destination Station unknown', - data: destination, - }; - } - - const trips = await tripSearch({ - start: startStation.evaNumber, - destination: destStation.evaNumber, - time, - maxChanges: 0, - }); - - const relevantTrip = trips.routes.find((t) => - t.segments.some( - (s) => - s.type === 'JNY' && - (s.train.number === trainNumber || - Boolean(s.wings?.some((w) => w.train.number === trainNumber))), - ), - ); - - return relevantTrip; -} - -const stopOccupancyCache = new Cache( - CacheDatabase.HafasStopOccupancy, -); - -async function stopOccupancy( - start: string, - destination: string, - trainNumber: string, - time: Date, - stopEva: string, -): Promise { - const keyWithouEva = `${start}-${destination}-${trainNumber}-${time}`; - const key = `${keyWithouEva}-${stopEva}`; - if (await stopOccupancyCache.exists(key)) { - return await stopOccupancyCache.get(key); - } - const relevantTrip = await getRelevantTrip( - start, - destination, - trainNumber, - time, - ); - - if (relevantTrip?.segments[0].type !== 'JNY') { - return; - } - - await Promise.all([ - relevantTrip.segments[0].stops.map((s) => - stopOccupancyCache.set( - `${keyWithouEva}-${s.station.evaNumber}`, - s.auslastung, - ), - ), - ]); - - return await stopOccupancyCache.get(key); -} diff --git a/src/server/HAFAS/profiles.ts b/src/server/HAFAS/profiles.ts deleted file mode 100644 index 4dcb82e67..000000000 --- a/src/server/HAFAS/profiles.ts +++ /dev/null @@ -1,367 +0,0 @@ -import Crypto from 'node:crypto'; - -function getSecret(hciChecksum: string) { - const enc = Buffer.from(hciChecksum, 'base64'); - const key = Buffer.from([ - 97, 72, 54, 70, 56, 122, 82, 117, 105, 66, 110, 109, 51, 51, 102, 85, - ]); - const iv = Buffer.alloc(16); - const cipher = Crypto.createDecipheriv('aes-128-cbc', key, iv); - const secret = cipher.update(enc, undefined, 'ascii') + cipher.final('ascii'); - - return secret; -} - -function createChecksumFn(secret: string) { - return (data: any) => { - const hasher = Crypto.createHash('md5'); - - hasher.update(JSON.stringify(data) + secret); - - return { - checksum: hasher.digest('hex'), - }; - }; -} - -function createMicMacFn(secret: string) { - return (data: any) => { - const micHasher = Crypto.createHash('md5'); - - micHasher.update(JSON.stringify(data)); - const mic = micHasher.digest('hex'); - const macHasher = Crypto.createHash('md5'); - - macHasher.update(mic + secret); - - return { mic, mac: macHasher.digest('hex') }; - }; -} - -export const vbn = { - url: 'https://fahrplaner.vbn.de/bin/mgate.exe', - secret: createMicMacFn( - getSecret('HLMpSMJpWe8wBXKtp2g1ey+XzkGxhYE3NsONGEmd8GE='), - ), - config: { - client: { - os: 'iOS 13.4.1', - id: 'VBN', - name: 'vbn', - type: 'IPH', - v: '6000000', - }, - ver: '1.18', - lang: 'de', - auth: { - aid: 'kaoxIXLn03zCr2KR', - type: 'AID', - }, - }, -}; - -export const smartrbl = { - url: 'https://db-smartrbl.hafas.de/mct/mgate', - config: { - client: { - id: 'HAFAS', - name: 'Test-Client', - type: 'WEB', - v: '1.0.0', - }, - lang: 'deu', - ver: '1.18', - auth: { - aid: 'izfpmpj8tnh6acye', - type: 'AID', - }, - }, -}; - -export const dbregio = { - url: 'https://bnav.hafas.de/bin/mgate.exe', - secret: createMicMacFn( - getSecret('scuNsYWPma+dzYl3K5cpjb+EsHxQJ/2VGnFwjInCQX4='), - ), - config: { - client: { - id: 'DB-REGIO-BNAV', - v: '3000500', - type: 'IPH', - name: 'StreckenagentPROD-APPSTORE', - }, - lang: 'de', - ext: 'DB.R19.12.a', - ver: '1.18', - auth: { - aid: 'Xd91BNAVkuI6rr6z', - type: 'AID', - }, - }, -}; - -export const db = { - url: 'https://reiseauskunft.bahn.de/bin/mgate.exe', - secret: createChecksumFn( - getSecret('rGhXPq+xAlvJd8T8cMnojdD0IoaOY53X7DPAbcXYe5g='), - ), - config: { - client: { - id: 'DB', - v: '20100000', - type: 'IPH', - name: 'DB Navigator', - }, - ext: 'DB.R22.04.a', - lang: 'de', - ver: '1.18', - auth: { - aid: 'n91dB8Z77MLdoR0K', - type: 'AID', - }, - }, -}; -// dummy; -export const bahn = db; - -export const pkp = { - url: 'https://mobil.rozklad-pkp.pl:8019/bin/mgate.exe', - config: { - client: { - id: 'HAFAS', - // v: '4010200', - type: 'AND', - // name: 'bilkomAPPSTORE', - }, - lang: 'en', - ver: '1.18', - auth: { - aid: 'DrxJYtYZQpEBCtcb', - type: 'AID', - }, - }, -}; - -export const rmv = { - url: 'https://www.rmv.de/auskunft/bin/jp/mgate.exe', - config: { - client: { - id: 'RMV', - l: 'vs_webapp', - type: 'WEB', - name: 'webapp', - }, - ext: 'RMV.1', - lang: 'de', - ver: '1.18', - auth: { - aid: 'x0k4ZR33ICN9CWmj', - type: 'AID', - }, - }, -}; - -export const oebb = { - url: 'https://fahrplan.oebb.at/bin/mgate.exe', - config: { - client: { - os: 'iOS 12.4', - id: 'OEBB', - v: '6020300', - type: 'IPH', - name: 'oebbADHOC', - }, - lang: 'de', - ver: '1.18', - auth: { - aid: 'OWDL4fE4ixNiPBBm', - type: 'AID', - }, - }, -}; - -export const sncb = { - url: 'http://www.belgianrail.be/jp/sncb-nmbs-routeplanner/mgate.exe', - config: { - client: { - os: 'iOS 12.4', - id: 'SNCB', - v: '4030200', - type: 'IPH', - name: 'sncb', - }, - lang: 'de', - ver: '1.18', - auth: { aid: 'sncb-mobi', type: 'AID' }, - }, -}; - -export const avv = { - url: 'https://auskunft.avv.de/bin/mgate.exe', - config: { - client: { - id: 'HAFAS', - type: 'WEB', - name: 'Test-Client', - v: '100', - }, - lang: 'deu', - ver: '1.18', - auth: { - type: 'AID', - aid: '4vV1AcH3N511icH', - }, - }, -}; - -export const nahsh = { - url: 'https://nah.sh.hafas.de/bin/mgate.exe', - config: { - client: { - os: 'iOS 12.4', - id: 'NAHSH', - v: '5000100', - type: 'IPH', - name: 'NAHSHPROD-APPSTORE', - }, - lang: 'de', - ver: '1.18', - auth: { aid: 'r0Ot9FLFNAFxijLW', type: 'AID' }, - }, -}; - -export const hvv = { - url: 'https://hvv-app.hafas.de/bin/mgate.exe', - secret: createMicMacFn( - getSecret('ktlwfW4vVOf/LwJ4wsnENvzRQZf3WS9b1RMPbIQNEOw='), - ), - config: { - client: { - os: 'iOS 12.4', - id: 'HVV', - v: '4020100', - type: 'IPH', - name: 'HVVPROD_ADHOC', - }, - lang: 'de', - ext: 'HVV.1', - ver: '1.18', - auth: { - aid: 'andcXUmC9Mq6hjrwDIGd2l3oiaMrTUzyH', - type: 'aid', - }, - }, -}; - -export const bvg = { - url: 'https://bvg-apps.hafas.de/bin/mgate.exe', - config: { - client: { - os: 'iOS 12.4', - id: 'BVG', - v: '6021600', - type: 'IPH', - name: 'Fahrinfo', - }, - lang: 'de', - ver: '1.18', - auth: { - aid: 'Mz0YdF9Fgx0Mb9', - type: 'AID', - }, - }, -}; - -export const insa = { - url: 'https://reiseauskunft.insa.de/bin/mgate.exe', - config: { - client: { - os: 'iOS 12.4.1', - id: 'NASA', - v: '4020300', - type: 'IPH', - name: 'nasaPROD-APPSTORE', - }, - lang: 'de', - ver: '1.18', - auth: { - aid: 'nasa-apps', - type: 'AID', - }, - }, -}; - -export const anachb = { - url: 'https://anachb.vor.at/bin/mgate.exe', - config: { - client: { - id: 'VAO', - type: 'WEB', - name: 'webapp', - }, - ver: '1.18', - lang: 'deu', - ext: 'VAO.10', - auth: { - type: 'AID', - aid: 'wf7mcf9bv3nv8g5f', - }, - }, -}; - -export const vao = { - url: 'http://app.verkehrsauskunft.at/bin/mgate.exe', - config: { - client: { - id: 'VAO', - type: 'IPH', - }, - ver: '1.18', - lang: 'deu', - ext: 'VAO.10', - auth: { - type: 'USER', - user: 'mobile', - pw: '87a6f8ZbnBih32', - aid: 'hf7mcf9bv3nv8g5f', - }, - }, -}; - -export const sbb = { - url: 'http://fahrplan.sbb.ch/bin/mgate.exe', - config: { - auth: { - aid: 'hf7mcf9bv3nv8g5f', - type: 'AID', - }, - client: { - id: 'DBZUGRADARNETZ', - type: 'AND', - v: '', - }, - ext: 'DBNETZZUGRADAR.2', - formatted: false, - lang: 'deu', - ver: '1.18', - }, -}; - -export const dbnetz = { - url: 'https://db-livemaps.hafas.de/bin/mgate.exe', - config: { - client: { - id: 'DBZUGRADARNETZ', - v: '0.1.0', - type: 'WEB', - name: 'webapp', - }, - ext: 'DBNETZZUGRADAR.2', - ver: '1.18', - auth: { - type: 'AID', - aid: 'hf7mcf9bv3nv8g5f', - }, - lang: 'deu', - }, -}; diff --git a/src/server/StopPlace/Lageplan/DBLageplan.ts b/src/server/StopPlace/Lageplan/DBLageplan.ts index 1478adc11..2a913045f 100644 --- a/src/server/StopPlace/Lageplan/DBLageplan.ts +++ b/src/server/StopPlace/Lageplan/DBLageplan.ts @@ -1,8 +1,8 @@ import { getStopPlaceByEva } from '@/server/StopPlace/search'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import Axios from 'axios'; -const cache = new Cache(CacheDatabase.DBLageplan); +const cache = getCache(CacheDatabase.DBLageplan); export async function getDBLageplan( evaNumber: string, diff --git a/src/server/StopPlace/Lageplan/NAHSHLageplan.ts b/src/server/StopPlace/Lageplan/NAHSHLageplan.ts deleted file mode 100644 index af5c69273..000000000 --- a/src/server/StopPlace/Lageplan/NAHSHLageplan.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { locMatch } from '@/server/HAFAS/LocMatch'; -import { Cache, CacheDatabase } from '@/server/cache'; -import { AllowedHafasProfile } from '@/types/HAFAS'; -import Axios from 'axios'; - -const cache = new Cache(CacheDatabase.NAHSHLageplan); - -function normalizeStationName(stationName: string) { - return stationName - .replace(' AKN', '') - .replace('ü', 'ue') - .replace('Ü', 'Ue') - .replace('ä', 'ae') - .replace('Ä', 'Ae') - .replace('ü', 'ue') - .replace('Ü', 'Ue') - .replace(' ', '_'); -} - -export async function getNAHSHLageplan( - evaId: string, -): Promise { - try { - const cached = await getCachedNAHSHLageplan(evaId); - if (cached) return cached; - if (cached === null) return undefined; - - const station = ( - await locMatch(evaId, 'S', AllowedHafasProfile['NAH.SH']) - )[0]; - if (station) { - const fullLink = `https://www.nah.sh/assets/downloads/Stationsplaene/${normalizeStationName( - station.name, - )}.pdf`; - - try { - await Axios.get(fullLink); - void cache.set(evaId, fullLink); - return fullLink; - } catch { - // we ignore failing requests and fall back to undefined for lageplanURL - } - } - void cache.set(evaId, null); - return undefined; - } catch { - return undefined; - } -} - -export function getCachedNAHSHLageplan( - evaId: string, -): Promise { - return cache.get(evaId); -} diff --git a/src/server/StopPlace/Lageplan/index.ts b/src/server/StopPlace/Lageplan/index.ts index cef5583fe..cf51856c1 100644 --- a/src/server/StopPlace/Lageplan/index.ts +++ b/src/server/StopPlace/Lageplan/index.ts @@ -1,11 +1,7 @@ import { getDBLageplan } from './DBLageplan'; -import { getNAHSHLageplan } from './NAHSHLageplan'; export async function getLageplan(evaNumber: string): Promise { - const [DBLageplan, NahSHLageplan] = await Promise.all([ - getDBLageplan(evaNumber), - getNAHSHLageplan(evaNumber), - ]); + const [DBLageplan] = await Promise.all([getDBLageplan(evaNumber)]); - return DBLageplan || NahSHLageplan || null; + return DBLageplan || null; } diff --git a/src/server/StopPlace/hafasSearch.ts b/src/server/StopPlace/hafasSearch.ts deleted file mode 100644 index 1d1532ce0..000000000 --- a/src/server/StopPlace/hafasSearch.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { locMatch } from '@/server/HAFAS/LocMatch'; -import { irisFilter } from '@/server/StopPlace/search'; -import { getSingleStation } from '@/server/iris/station'; -import type { GroupedStopPlace } from '@/types/stopPlace'; - -async function byRl100(rl100: string): Promise { - if (rl100.length > 5) { - return; - } - try { - const irisRl100 = await getSingleStation(rl100); - return { - name: irisRl100.name, - evaNumber: irisRl100.eva, - availableTransports: [], - ril100: irisRl100.ds100, - }; - } catch { - // we just return nothing on fail - } -} - -export async function searchWithHafas( - searchTerm?: string, - max?: number, - filterForIris?: boolean, -): Promise { - if (!searchTerm) return []; - const results = await Promise.all([ - await locMatch(searchTerm, 'S'), - byRl100(searchTerm), - ]); - let hafasResult = results[0]; - const rl100ByIris = results[1]; - if (max) { - hafasResult = hafasResult.splice(0, max); - } - - const result: GroupedStopPlace[] = hafasResult - .filter((s) => s.evaNumber.length <= 7 || !s.evaNumber.startsWith('99')) - .map((s) => ({ - evaNumber: s.evaNumber, - name: s.name, - position: { - latitude: s.coordinates.lat, - longitude: s.coordinates.lng, - }, - availableTransports: [], - })); - - if (rl100ByIris) { - result.unshift(rl100ByIris); - } - - if (filterForIris) { - return irisFilter(result); - } - return result; -} diff --git a/src/server/StopPlace/search.ts b/src/server/StopPlace/search.ts index ee21e0178..05ad8dbb9 100644 --- a/src/server/StopPlace/search.ts +++ b/src/server/StopPlace/search.ts @@ -5,31 +5,20 @@ import type { StopPlace, StopPlaceSearchResult, } from '@/external/types'; -import { searchWithHafas } from '@/server/StopPlace/hafasSearch'; import { manualNameOverrides } from '@/server/StopPlace/manualNameOverrides'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { getSingleStation } from '@/server/iris/station'; import type { GroupedStopPlace, StopPlaceIdentifier } from '@/types/stopPlace'; -const stopPlaceStationSearchCache = new Cache( - CacheDatabase.StopPlaceSearch, -); -const stopPlaceSalesSearchCache = new Cache( - CacheDatabase.StopPlaceSalesSearch, -); -const stopPlaceIdentifierCache = new Cache( - CacheDatabase.StopPlaceIdentifier, -); -const stopPlaceByRilCache = new Cache(CacheDatabase.StopPlaceByRil); -const stopPlaceByRilGroupedCache = new Cache( +const stopPlaceStationSearchCache = getCache(CacheDatabase.StopPlaceSearch); +const stopPlaceSalesSearchCache = getCache(CacheDatabase.StopPlaceSalesSearch); +const stopPlaceIdentifierCache = getCache(CacheDatabase.StopPlaceIdentifier); +const stopPlaceByRilCache = getCache(CacheDatabase.StopPlaceByRil); +const stopPlaceByRilGroupedCache = getCache( CacheDatabase.StopPlaceByRilGrouped, ); -const stopPlaceByEvaCache = new Cache( - CacheDatabase.StopPlaceByEva, -); -const stopPlaceGroupCache = new Cache( - CacheDatabase.StopPlaceGroups, -); +const stopPlaceByEvaCache = getCache(CacheDatabase.StopPlaceByEva); +const stopPlaceGroupCache = getCache(CacheDatabase.StopPlaceGroups); function mapToGroupedStopPlace( stopPlace: Pick< @@ -68,18 +57,12 @@ export async function searchStopPlace( filterForIris?: boolean, groupBySales?: boolean, ): Promise { - try { - const result = await searchStopPlaceRisStations( - searchTerm, - max, - filterForIris, - groupBySales, - ); - if (result?.length || filterForIris || groupBySales) return result; - return searchWithHafas(searchTerm, max, filterForIris); - } catch { - return searchWithHafas(searchTerm, max, filterForIris); - } + return await searchStopPlaceRisStations( + searchTerm, + max, + filterForIris, + groupBySales, + ); } export async function searchStopPlaceRisStations( @@ -128,17 +111,6 @@ export async function getStopPlaceByEva( void stopPlaceByEvaCache.set(groupedStopPlace.evaNumber, groupedStopPlace); return groupedStopPlace; } - if (!forceLive) { - const hafasResults = await searchWithHafas(evaNumber, 1, false); - const groupedHafasResult = hafasResults[0]; - if (groupedHafasResult && groupedHafasResult.evaNumber === evaNumber) { - void stopPlaceByEvaCache.set( - groupedHafasResult.evaNumber, - groupedHafasResult, - ); - return groupedHafasResult; - } - } } async function byRl100WithSpaceHandling( diff --git a/src/server/StopPlace/vrrOccupancy.ts b/src/server/StopPlace/vrrOccupancy.ts index 626286abd..65d7525fa 100644 --- a/src/server/StopPlace/vrrOccupancy.ts +++ b/src/server/StopPlace/vrrOccupancy.ts @@ -1,5 +1,5 @@ import { axiosUpstreamInterceptor } from '@/server/admin'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { AuslastungsValue } from '@/types/routing'; import type { TrainOccupancy, @@ -12,9 +12,7 @@ import axios from 'axios'; const occupancyAxios = axios.create(); axiosUpstreamInterceptor(occupancyAxios, 'vrrf-occupancy'); -const vrrOccupancyCache = new Cache( - CacheDatabase.VRROccupancy, -); +const vrrOccupancyCache = getCache(CacheDatabase.VRROccupancy); function mapVrrOccupancy( vrrOccupancy: VRRTrainOccupancyValues, diff --git a/src/server/admin/index.ts b/src/server/admin/index.ts index 0dffabee7..2629a8c7d 100644 --- a/src/server/admin/index.ts +++ b/src/server/admin/index.ts @@ -1,4 +1,4 @@ -import { type Server, createServer } from 'node:http'; +import { createServer } from 'node:http'; import { type AxiosInstance, type AxiosResponse, @@ -7,32 +7,47 @@ import { } from 'axios'; import PromClient, { Counter, Histogram } from 'prom-client'; -PromClient.register.clear(); -PromClient.collectDefaultMetrics(); - -export const ApiRequestMetric = new Histogram({ - name: 'api_requests', - help: 'api requests', - labelNames: ['route', 'status'], -}); +export let ApiRequestMetric = PromClient.register.getSingleMetric( + 'api_requests', +) as Histogram | undefined; +if (!ApiRequestMetric) { + ApiRequestMetric = new Histogram({ + name: 'api_requests', + help: 'api requests', + labelNames: ['route', 'status'], + }); + PromClient.register.registerMetric(ApiRequestMetric); +} -export const UpstreamApiRequestMetric = new Counter({ - name: 'upstream_api_requests', - help: 'upstream api requests => bahn', - labelNames: ['api'], -}); +export let UpstreamApiRequestMetric = PromClient.register.getSingleMetric( + 'upstream_api_requests', +) as Counter | undefined; +if (!UpstreamApiRequestMetric) { + UpstreamApiRequestMetric = new Counter({ + name: 'upstream_api_requests', + help: 'upstream api requests => bahn', + labelNames: ['api'], + }); + PromClient.register.registerMetric(UpstreamApiRequestMetric); +} -const UpstreamApiResponseMetric = new Counter({ - name: 'upstream_api_response', - help: 'upstream api response => bahn', - labelNames: ['api', 'code'], -}); +let UpstreamApiResponseMetric = PromClient.register.getSingleMetric( + 'upstream_api_response', +) as Counter | undefined; +if (!UpstreamApiResponseMetric) { + UpstreamApiResponseMetric = new Counter({ + name: 'upstream_api_response', + help: 'upstream api response => bahn', + labelNames: ['api', 'code'], + }); + PromClient.register.registerMetric(UpstreamApiResponseMetric); +} function upstreamRequestApiCountInterceptor( apiName: string, req: InternalAxiosRequestConfig, ): InternalAxiosRequestConfig { - UpstreamApiRequestMetric.inc({ api: apiName }); + UpstreamApiRequestMetric?.inc({ api: apiName }); return req; } @@ -40,13 +55,13 @@ function upstreamResponseApiCountInterceptor( apiName: string, res: AxiosResponse, ): AxiosResponse { - UpstreamApiResponseMetric.inc({ api: apiName, code: res.status }); + UpstreamApiResponseMetric?.inc({ api: apiName, code: res.status }); return res; } function upstreamErrorResponseApiCountInterceptor(apiName: string, error: any) { if (isAxiosError(error) && error.status) { - UpstreamApiResponseMetric.inc({ api: apiName, code: error.status }); + UpstreamApiResponseMetric?.inc({ api: apiName, code: error.status }); } return error; } @@ -64,8 +79,12 @@ export function axiosUpstreamInterceptor( ); } -export default (adminPort = 9000): Server => { - return createServer(async (req, res) => { +export default () => { + PromClient.collectDefaultMetrics(); + if (globalThis.adminServer) { + globalThis.adminServer.close(); + } + globalThis.adminServer = createServer(async (req, res) => { try { switch (req.url) { case '/ping': @@ -74,11 +93,14 @@ export default (adminPort = 9000): Server => { case '/metrics': res.write(await PromClient.register.metrics()); break; + default: + res.statusCode = 404; + break; } } catch { res.statusCode = 500; } finally { res.end(); } - }).listen(adminPort); + }).listen(9000); }; diff --git a/src/server/admin/nitroPlugin.ts b/src/server/admin/nitroPlugin.ts deleted file mode 100644 index 8aeab4a8a..000000000 --- a/src/server/admin/nitroPlugin.ts +++ /dev/null @@ -1,8 +0,0 @@ -import createAdminServer from './index'; - -export default () => { - if (globalThis.adminServer) { - globalThis.adminServer.close(); - } - globalThis.adminServer = createAdminServer(); -}; diff --git a/src/server/cache.ts b/src/server/cache.ts index eb8fc3770..5ea92bd5e 100644 --- a/src/server/cache.ts +++ b/src/server/cache.ts @@ -1,5 +1,32 @@ import os from 'node:os'; +import type { + JourneyEventBased as JourneyEventBasedV1, + JourneyMatch, +} from '@/external/generated/risJourneys'; +import type { + JourneyEventBased, + JourneyFindResult, +} from '@/external/generated/risJourneysV2'; +import type { VehicleLayoutFeatureCollection } from '@/external/generated/risMaps'; +import type { StopPlace } from '@/external/generated/risStations'; +import type { + JourneyOccupancy, + MatchVehicleID, +} from '@/external/generated/risTransports'; +import type { ResolvedStopPlaceGroups } from '@/external/types'; import { logger } from '@/server/logger'; +import type { + CoachSequenceCoachFeatures, + CoachSequenceInformation, +} from '@/types/coachSequence'; +import type { IrisStation } from '@/types/iris'; +import type { JourneyFindResponse, JourneyResponse } from '@/types/journey'; +import type { RouteAuslastung } from '@/types/routing'; +import type { + GroupedStopPlace, + StopPlaceIdentifier, + TrainOccupancyList, +} from '@/types/stopPlace'; import { Temporal } from '@js-temporal/polyfill'; import Redis from 'ioredis'; @@ -49,9 +76,9 @@ export enum CacheDatabase { IrisTTSStation = 0, TimetableParsedWithWings = 1, DBLageplan = 2, - LocMatch = 3, - HIMMessage = 4, - NAHSHLageplan = 5, + // LocMatch = 3, + // HIMMessage = 4, + // NAHSHLageplan = 5, StopPlaceSearch = 6, StopPlaceByRilGrouped = 7, StopPlaceIdentifier = 8, @@ -63,7 +90,7 @@ export enum CacheDatabase { NegativeNewSequence = 14, TransportsOccupancy = 15, HafasStopOccupancy = 16, - AdditionalJourneyInformation = 17, + // AdditionalJourneyInformation = 17, HAFASJourneyMatch = 18, Journey = 19, JourneysForVehicle = 20, @@ -76,13 +103,52 @@ export enum CacheDatabase { BahnDEJourneyDetails = 27, } -const CacheTTLs: Record = { +interface CacheDatabaseTypes { + // [CacheDatabase.AdditionalJourneyInformation]: + // | AdditionalJourneyInformation + // | undefined; + [CacheDatabase.BahnDEJourneyDetails]: JourneyResponse; + [CacheDatabase.CoachSequenceRemovedData]: Record< + string, + { + identificationNumber: string; + features: CoachSequenceCoachFeatures; + } + >; + [CacheDatabase.DBLageplan]: string | null; + [CacheDatabase.HAFASJourneyMatch]: JourneyFindResponse[]; + [CacheDatabase.HafasStopOccupancy]: RouteAuslastung | undefined; + [CacheDatabase.IrisTTSStation]: IrisStation | null; + [CacheDatabase.Journey]: JourneyEventBasedV1; + [CacheDatabase.JourneyFind]: JourneyMatch[]; + [CacheDatabase.JourneyFindV2]: JourneyFindResult[]; + [CacheDatabase.JourneyV2]: JourneyEventBased; + [CacheDatabase.JourneysForVehicle]: { + previousJourneys: MatchVehicleID[]; + nextJourneys: MatchVehicleID[]; + }; + [CacheDatabase.NegativeNewSequence]: boolean; + [CacheDatabase.ParsedCoachSequenceFound]: CoachSequenceInformation; + [CacheDatabase.StopPlaceByEva]: GroupedStopPlace; + [CacheDatabase.StopPlaceByRil]: StopPlace; + [CacheDatabase.StopPlaceByRilGrouped]: GroupedStopPlace; + [CacheDatabase.StopPlaceGroups]: ResolvedStopPlaceGroups; + [CacheDatabase.StopPlaceIdentifier]: StopPlaceIdentifier | undefined; + [CacheDatabase.StopPlaceSalesSearch]: GroupedStopPlace[]; + [CacheDatabase.StopPlaceSearch]: GroupedStopPlace[]; + [CacheDatabase.TimetableParsedWithWings]: { + timetable: Record; + wingIds: Record; + }; + [CacheDatabase.TransportsOccupancy]: JourneyOccupancy | undefined; + [CacheDatabase.VRROccupancy]: TrainOccupancyList | null; + [CacheDatabase.VehicleLayoutsMaps]: VehicleLayoutFeatureCollection | null; +} + +const CacheTTLs: Record = { [CacheDatabase.IrisTTSStation]: 'P2D', [CacheDatabase.TimetableParsedWithWings]: 'P1D', [CacheDatabase.DBLageplan]: 'P1D', - [CacheDatabase.LocMatch]: 'P1D', - [CacheDatabase.HIMMessage]: 'P1D', - [CacheDatabase.NAHSHLageplan]: 'P3D', [CacheDatabase.StopPlaceSearch]: 'P3D', [CacheDatabase.ParsedCoachSequenceFound]: parseCacheTTL( 'PT15M', @@ -97,7 +163,7 @@ const CacheTTLs: Record = { [CacheDatabase.HAFASJourneyMatch]: 'P2D', [CacheDatabase.NegativeNewSequence]: 'PT6H', [CacheDatabase.HafasStopOccupancy]: 'PT30M', - [CacheDatabase.AdditionalJourneyInformation]: 'PT10M', + // [CacheDatabase.AdditionalJourneyInformation]: 'PT10M', [CacheDatabase.JourneyFind]: 'P2D', [CacheDatabase.JourneyFindV2]: 'P2D', [CacheDatabase.Journey]: parseCacheTTL( @@ -124,7 +190,21 @@ export function disconnectRedis(): void { } } -export class Cache { +const caches: Partial>> = {}; + +export function getCache( + type: T, +): Cache { + if (!caches[type]) { + caches[type] = new Cache(type); + } + return caches[type]; +} + +export type CacheType> = C extends Cache + ? X + : never; +class Cache { private redisCache?: Redis; private ttl: number; constructor(database: CacheDatabase, providedRedisSettings = redisSettings) { diff --git a/src/server/coachSequence/DB/bahnDe/index.ts b/src/server/coachSequence/DB/bahnDe/index.ts index 4521d68b7..cc28fc064 100644 --- a/src/server/coachSequence/DB/bahnDe/index.ts +++ b/src/server/coachSequence/DB/bahnDe/index.ts @@ -22,7 +22,7 @@ export async function getNewDBCoachSequence( initialDepartureDate: Date, ): Promise { try { - UpstreamApiRequestMetric.inc({ + UpstreamApiRequestMetric?.inc({ api: 'coachSequence-bahnde', }); const sequence = ( diff --git a/src/server/coachSequence/DB/index.ts b/src/server/coachSequence/DB/index.ts index b9035bb7f..4436fb568 100644 --- a/src/server/coachSequence/DB/index.ts +++ b/src/server/coachSequence/DB/index.ts @@ -1,17 +1,13 @@ import { isWithin20Hours } from '@/external/risTransports/config'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { getRisTransportsCoachSequence } from '@/server/coachSequence/DB/risTransports'; import type { CoachSequenceInformation } from '@/types/coachSequence'; import { format } from 'date-fns'; const formatDate = (date?: Date) => date ? format(date, 'yyyyMMddHHmm') : undefined; -// const formatPlannedDate = (date?: Date) => -// date ? format(utcToZonedTime(date, 'Europe/Berlin'), 'yyyyMMdd') : undefined; -const coachSequenceCache = new Cache( - CacheDatabase.ParsedCoachSequenceFound, -); +const coachSequenceCache = getCache(CacheDatabase.ParsedCoachSequenceFound); export async function DBCoachSequence( trainNumber: string, diff --git a/src/server/coachSequence/DB/removedData.ts b/src/server/coachSequence/DB/removedData.ts index c9d1e4c66..84f58034b 100644 --- a/src/server/coachSequence/DB/removedData.ts +++ b/src/server/coachSequence/DB/removedData.ts @@ -1,21 +1,8 @@ -import { Cache, CacheDatabase } from '@/server/cache'; -import type { - CoachSequenceCoachFeatures, - CoachSequenceInformation, -} from '@/types/coachSequence'; +import { CacheDatabase, type CacheType, getCache } from '@/server/cache'; +import type { CoachSequenceInformation } from '@/types/coachSequence'; -// evn => removedData for a specific journeyId -type RemovedData = Record< - string, - { - identificationNumber: string; - features: CoachSequenceCoachFeatures; - } ->; - -const removedDataCache = new Cache( - CacheDatabase.CoachSequenceRemovedData, -); +const removedDataCache = getCache(CacheDatabase.CoachSequenceRemovedData); +type RemovedData = CacheType; export async function fixRemovedData( risTransportsSequence: CoachSequenceInformation, diff --git a/src/server/coachSequence/DB/trainRuns.ts b/src/server/coachSequence/DB/trainRuns.ts index d87770551..c8fe87746 100644 --- a/src/server/coachSequence/DB/trainRuns.ts +++ b/src/server/coachSequence/DB/trainRuns.ts @@ -6,7 +6,6 @@ import { } from '@/server/coachSequence/DB/plannedSequence'; import { getLineFromNumber } from '@/server/journeys/lineNumberMapping'; import type { AvailableBR, AvailableIdentifier } from '@/types/coachSequence'; -import type { EvaNumber } from '@/types/common'; import type { TrainRun, TrainRunWithBR } from '@/types/trainRuns'; export async function getSingleTrainRun( @@ -31,7 +30,7 @@ export async function getTrainRunsByDate( date: Date, baureihen?: AvailableBR[], brIdentifier?: AvailableIdentifier[], - stopsAt?: EvaNumber[], + stopsAt?: string[], ): Promise { try { const trainRuns = ( diff --git a/src/server/coachSequence/index.ts b/src/server/coachSequence/index.ts index a230587e2..9d7214780 100644 --- a/src/server/coachSequence/index.ts +++ b/src/server/coachSequence/index.ts @@ -1,6 +1,5 @@ import { DBCoachSequence } from '@/server/coachSequence/DB'; import type { CoachSequenceInformation } from '@/types/coachSequence'; -import type { EvaNumber } from '@/types/common'; import { addDays, differenceInHours, @@ -11,7 +10,7 @@ import { export async function coachSequence( trainNumber: string, departure: Date, - evaNumber: EvaNumber, + evaNumber: string, trainCategory: string, administration?: string, ): Promise { diff --git a/src/server/coachSequence/occupancy.ts b/src/server/coachSequence/occupancy.ts index 839a3fd86..ff2004a1b 100644 --- a/src/server/coachSequence/occupancy.ts +++ b/src/server/coachSequence/occupancy.ts @@ -1,7 +1,7 @@ import type { Occupancy } from '@/external/generated/risTransports'; import { getJourneyOccupancy } from '@/external/risTransports/occupancy'; import { journeyDetails as fetchJourneyDetails } from '@/server/journeys/v2/journeyDetails'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; import { AuslastungsValue, type RouteAuslastung } from '@/types/routing'; export function mapSingleOccupancy( @@ -30,7 +30,7 @@ function mapOccupancy(stopOccupancy: Occupancy): RouteAuslastung { export async function getOccupancy( journeyId: string, - providedJourneyDetails?: ParsedSearchOnTripResponse, + providedJourneyDetails?: JourneyResponse, ) { const journeyDetails = providedJourneyDetails || (await fetchJourneyDetails(journeyId)); diff --git a/src/server/iris/Timetable.ts b/src/server/iris/Timetable.ts index 808731f47..c5f44999c 100644 --- a/src/server/iris/Timetable.ts +++ b/src/server/iris/Timetable.ts @@ -3,14 +3,12 @@ ** derf did awesome work reverse engineering the XML stuff! */ import { uniqBy } from '@/client/util'; -import { getSingleHimMessageOfToday } from '@/server/HAFAS/HimSearch'; import { getStopPlaceByEva } from '@/server/StopPlace/search'; -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { getSingleStation } from '@/server/iris/station'; import { getLineFromNumber } from '@/server/journeys/lineNumberMapping'; import type { AbfahrtenResult, - HimIrisMessage, IrisMessage, IrisMessages, Stop, @@ -66,10 +64,7 @@ type ParsedAr = ArDp & { plannedRoutePre?: string[]; }; -const timetableCache = new Cache<{ - timetable: Record; - wingIds: Record; -}>(CacheDatabase.TimetableParsedWithWings); +const timetableCache = getCache(CacheDatabase.TimetableParsedWithWings); interface Route { name: string; @@ -404,90 +399,13 @@ export class Timetable { name, }; } - async parseHafasMessage( - mNode: xmljs.Element, - viaNames: string[], - seenHafasNotes: Set, - ) { - const id = getAttr(mNode, 'id'); - - if (!id) return undefined; - - const himMessage = await getSingleHimMessageOfToday(id.slice(1)); - - if (!himMessage) return undefined; - // Sadly this is not accurate. Often affected Products is not corectly set - // if (!himMessage.affectedProducts.some((p) => p.name.endsWith(trainNumber))) - // return undefined; - const now = new Date(); - - if ( - isBefore(now, himMessage.startTime) || - isAfter(now, himMessage.endTime) - ) { - return undefined; - } - - const message: HimIrisMessage = { - timestamp: getTsOfNode(mNode), - head: himMessage.head, - text: himMessage.text, - // short: himMessage.lead === himMessage.text ? undefined : himMessage.lead, - source: himMessage.comp, - endTime: himMessage.endTime, - // @ts-expect-error raw only in dev - raw: process.env.NODE_ENV === 'production' ? undefined : himMessage, - }; - - if (himMessage.fromStopPlace && himMessage.toStopPlace) { - const fromIndex = viaNames.indexOf(himMessage.fromStopPlace.name); - const toIndex = viaNames.indexOf(himMessage.toStopPlace.name); - if (fromIndex !== -1 && toIndex !== -1) { - const from = - fromIndex < toIndex - ? himMessage.fromStopPlace.name - : himMessage.toStopPlace.name; - const to = - fromIndex > toIndex - ? himMessage.fromStopPlace.name - : himMessage.toStopPlace.name; - - let stopPlaceInfo = from; - if (from !== to) { - stopPlaceInfo += ` - ${to}`; - } - message.stopPlaceInfo = stopPlaceInfo; - } - } - - const hafasMessageKey = message.text + message.stopPlaceInfo; - if (seenHafasNotes.has(hafasMessageKey)) { - return undefined; - } - seenHafasNotes.add(hafasMessageKey); - - return { - type: 'him', - value: id, - message, - }; - } - async parseMessage( - mNode: xmljs.Element, - viaNames: string[], - seenHafasNotes: Set, - ) { + async parseMessage(mNode: xmljs.Element) { const value = getNumberAttr(mNode, 'c'); const indexType = getAttr(mNode, 't'); if (!indexType) return undefined; if (indexType === 'h') { - const message = await this.parseHafasMessage( - mNode, - viaNames, - seenHafasNotes, - ); - return message; + return undefined; } const type: undefined | string = messageTypeLookup[indexType as keyof typeof messageTypeLookup]; @@ -555,11 +473,8 @@ export class Timetable { him: {}, }; - const seenHafasNotes = new Set(); const parsedMessages = await Promise.all( - mArr.map((m) => - this.parseMessage(m, this.timetable[rawId].rawRoute, seenHafasNotes), - ), + mArr.map((m) => this.parseMessage(m)), ); for (const { type, message, value } of parsedMessages diff --git a/src/server/iris/__tests__/onlyPlan.test.ts b/src/server/iris/__tests__/onlyPlan.test.ts index 5c80ec515..4bc8f9a61 100644 --- a/src/server/iris/__tests__/onlyPlan.test.ts +++ b/src/server/iris/__tests__/onlyPlan.test.ts @@ -8,8 +8,6 @@ import { import { Timetable } from '@/server/iris/Timetable'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; -vi.mock('@/server/cache'); - describe('onlyPlan', () => { beforeAll(() => { vi.useFakeTimers({ diff --git a/src/server/iris/__tests__/withFchg.test.ts b/src/server/iris/__tests__/withFchg.test.ts index 754344105..d4d199253 100644 --- a/src/server/iris/__tests__/withFchg.test.ts +++ b/src/server/iris/__tests__/withFchg.test.ts @@ -8,8 +8,6 @@ import { import { Timetable } from '@/server/iris/Timetable'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; -vi.mock('@/server/cache'); - describe('withFchg', () => { beforeAll(() => { vi.useFakeTimers({ diff --git a/src/server/iris/station.ts b/src/server/iris/station.ts index 232a8730c..6a6e7154e 100644 --- a/src/server/iris/station.ts +++ b/src/server/iris/station.ts @@ -1,11 +1,11 @@ -import { Cache, CacheDatabase } from '@/server/cache'; +import { CacheDatabase, getCache } from '@/server/cache'; import { stationMetaFilter } from '@/server/iris/stationMetaFilter'; import type { IrisStation, IrisStationWithRelated } from '@/types/iris'; import xmljs from 'libxmljs2'; import type { Element } from 'libxmljs2'; import { irisGetRequest } from './helper'; -const cache = new Cache(CacheDatabase.IrisTTSStation); +const cache = getCache(CacheDatabase.IrisTTSStation); export function parseStation(stationNode: xmljs.Element): IrisStation { const station: any = {}; diff --git a/src/server/journeys/additionalJourneyInformation.ts b/src/server/journeys/additionalJourneyInformation.ts deleted file mode 100644 index 292f58b6c..000000000 --- a/src/server/journeys/additionalJourneyInformation.ts +++ /dev/null @@ -1,72 +0,0 @@ -import Detail from '@/server/HAFAS/Detail'; -import { Cache, CacheDatabase } from '@/server/cache'; -import { getOccupancy } from '@/server/coachSequence/occupancy'; -import { journeyDetails } from '@/server/journeys/v2/journeyDetails'; -import type { AdditionalJourneyInformation } from '@/types/HAFAS/JourneyDetails'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; -import type { EvaNumber } from '@/types/common'; -import type { RouteAuslastung } from '@/types/routing'; - -const additionalInformationCache = new Cache< - AdditionalJourneyInformation | undefined ->(CacheDatabase.AdditionalJourneyInformation); - -async function enrichTransportOccupancy( - journeyId: string, - additionalInformation: AdditionalJourneyInformation, - providedJourneyDetails?: ParsedSearchOnTripResponse, -) { - const occupancy = await getOccupancy(journeyId, providedJourneyDetails); - if (occupancy) { - additionalInformation.occupancy = occupancy; - } -} - -/** - * This currently queries HAFAS to get operatorNames & occupancy - */ -export async function additionalJourneyInformation( - trainName: string, - journeyId: string, - evaNumberAlongRoute?: string, - initialDepartureDate?: Date, -): Promise { - if (await additionalInformationCache.exists(journeyId)) { - const additionalInformation = - await additionalInformationCache.get(journeyId); - if (additionalInformation) { - await enrichTransportOccupancy(journeyId, additionalInformation); - } - return additionalInformation; - } - const risJourneyDetails = await journeyDetails(journeyId); - const firstUncancelledStop = risJourneyDetails?.stops.find( - (s) => !s.departure?.cancelled, - ); - const hafasJourneyDetails = await Detail( - trainName, - undefined, - firstUncancelledStop?.station.evaNumber || evaNumberAlongRoute, - firstUncancelledStop?.departure?.scheduledTime || initialDepartureDate, - true, - ); - if (!hafasJourneyDetails) { - return; - } - - const occupancy: Record = {}; - for (const stop of hafasJourneyDetails.stops) { - if (stop.auslastung) { - occupancy[stop.station.evaNumber] = stop.auslastung; - } - } - const result: AdditionalJourneyInformation = { - jid: hafasJourneyDetails.jid, - occupancy, - operatorName: hafasJourneyDetails.train.operator?.name, - polyline: hafasJourneyDetails.polyline, - }; - void additionalInformationCache.set(journeyId, result); - await enrichTransportOccupancy(journeyId, result, risJourneyDetails); - return result; -} diff --git a/src/server/journeys/journeyDetails.ts b/src/server/journeys/journeyDetails.ts index 9e9a0d542..ed0f18cca 100644 --- a/src/server/journeys/journeyDetails.ts +++ b/src/server/journeys/journeyDetails.ts @@ -2,13 +2,13 @@ import { EventType, TimeType } from '@/external/generated/risJourneys'; import type { ArrivalDepartureEvent } from '@/external/generated/risJourneys'; import type { TransportDestinationPortionWorkingRef } from '@/external/generated/risJourneysV2'; import { getJourneyDetails } from '@/external/risJourneys'; -import { calculateCurrentStopPlace } from '@/server/HAFAS/Detail'; import { getStopPlaceByEva } from '@/server/StopPlace/search'; import { getAbfahrten } from '@/server/iris'; import { getLineFromNumber } from '@/server/journeys/lineNumberMapping'; -import type { CommonStopInfo } from '@/types/HAFAS'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import { calculateCurrentStopPlace } from '@/server/journeys/v2/journeyDetails'; +import type { JourneyResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; +import type { CommonStopInfo } from '@/types/stopPlace'; import { compareDesc, differenceInMinutes, @@ -22,7 +22,7 @@ import { const trainNumberRegex = /(.*?)(\d+).*/; export async function addIrisMessagesToDetails( - details: ParsedSearchOnTripResponse, + details: JourneyResponse, ): Promise { const irisStop = details.currentStop || details.stops.at(-1); @@ -227,7 +227,7 @@ async function stopsFromEvents( export async function journeyDetails( journeyId: string, -): Promise { +): Promise { const journey = await getJourneyDetails(journeyId); if (!journey?.events?.length) { return undefined; @@ -248,7 +248,7 @@ export async function journeyDetails( ...new Set(journey.events.map((e) => e.administration.operatorName)), ].join(', '); - const result: ParsedSearchOnTripResponse = { + const result: JourneyResponse = { stops, segmentStart: firstStop.station, segmentDestination: lastStop.station, @@ -265,9 +265,7 @@ export async function journeyDetails( admin: firstEvent.administration.administrationID, line: getLineFromNumber(firstEvent.transport.number.toString()), transportType: firstEvent.transport.type, - operator: { - name: operatorNames, - }, + operator: operatorNames, }, type: 'JNY', cancelled: stops.every((s) => s.cancelled) || undefined, diff --git a/src/server/journeys/v2/journeyDetails.ts b/src/server/journeys/v2/journeyDetails.ts index 29ce614f2..55341514b 100644 --- a/src/server/journeys/v2/journeyDetails.ts +++ b/src/server/journeys/v2/journeyDetails.ts @@ -7,13 +7,12 @@ import type { TransportWithDirection, } from '@/external/generated/risJourneysV2'; import { getJourneyDetails } from '@/external/risJourneysV2'; -import { calculateCurrentStopPlace } from '@/server/HAFAS/Detail'; import { getStopPlaceByEva } from '@/server/StopPlace/search'; import { addIrisMessagesToDetails } from '@/server/journeys/journeyDetails'; import { getLineFromNumber } from '@/server/journeys/lineNumberMapping'; -import type { CommonStopInfo } from '@/types/HAFAS'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; import type { RouteStop } from '@/types/routing'; +import type { CommonStopInfo } from '@/types/stopPlace'; import { addHours, differenceInMinutes, @@ -24,6 +23,21 @@ import { } from 'date-fns'; import administrationNames from '../names.json'; +export function calculateCurrentStopPlace( + segment: JourneyResponse, +): RouteStop | undefined { + const currentDate = Date.now(); + + return segment.stops.find((s) => { + const stopInfo = + s.departure && !s.departure.cancelled ? s.departure : s.arrival; + + return ( + stopInfo && !stopInfo.cancelled && isAfter(stopInfo.time, currentDate) + ); + }); +} + interface StopInfoWithAdditional extends CommonStopInfo { additional?: boolean; travelsWith?: TransportDestinationPortionWorkingRef[]; @@ -243,7 +257,7 @@ async function stopsFromEvents(events: JourneyEvent[]): Promise { export async function journeyDetails( journeyId: string, -): Promise { +): Promise { const journey = await getJourneyDetails(journeyId); if (!journey?.events?.length) { return undefined; @@ -272,7 +286,7 @@ export async function journeyDetails( ), ].join(', '); - const result: ParsedSearchOnTripResponse = { + const result: JourneyResponse = { stops, segmentStart: firstStop.station, segmentDestination: lastStop.station, @@ -289,9 +303,7 @@ export async function journeyDetails( admin: firstEvent.transport.administration.administrationID, line: getLineFromNumber(firstEvent.transport.journeyNumber.toString()), transportType: firstEvent.transport.type, - operator: { - name: operatorNames, - }, + operator: operatorNames, }, type: 'JNY', cancelled: stops.every((s) => s.cancelled) || undefined, diff --git a/src/server/logger/axiosLogging.ts b/src/server/logger/axiosLogging.ts index be715ee71..604f4c06c 100644 --- a/src/server/logger/axiosLogging.ts +++ b/src/server/logger/axiosLogging.ts @@ -17,13 +17,6 @@ Axios.defaults.transformRequest.push(function (data, _headers) { method: this.method?.toUpperCase(), url, }; - if (this.method?.toUpperCase() === 'POST') { - try { - logParams.hafasMethod = JSON.parse(data)?.svcReqL?.at(0)?.meth; - } catch { - //ignore - most likely json parse error - } - } logger.debug(logParams, 'Request'); if (data) { return data; diff --git a/src/server/rpc/base.ts b/src/server/rpc/base.ts index a2a4d552d..8e8c5cd18 100644 --- a/src/server/rpc/base.ts +++ b/src/server/rpc/base.ts @@ -18,7 +18,7 @@ const t = initTRPC export const rpcAppRouter = t.router; export const rpcProcedure = t.procedure.use(async (opts) => { - const end = ApiRequestMetric.startTimer(); + const end = ApiRequestMetric?.startTimer()!; const result = await opts.next(); const metricOptions: Parameters[number] = { route: opts.path, @@ -27,7 +27,7 @@ export const rpcProcedure = t.procedure.use(async (opts) => { if (!result.ok) { metricOptions.status = getHTTPStatusCodeFromError(result.error); } - end(metricOptions); + end?.(metricOptions); return result; }); diff --git a/src/server/rpc/hafas.ts b/src/server/rpc/hafas.ts index 4bcb8a580..7d1ce1b9c 100644 --- a/src/server/rpc/hafas.ts +++ b/src/server/rpc/hafas.ts @@ -1,75 +1,12 @@ import { bahnDeOccupancy } from '@/bahnde/occupancy'; -import StationBoard from '@/server/HAFAS/StationBoard'; -import StationBoardToTimetables from '@/server/HAFAS/StationBoard/StationBoardToTimetables'; -import { tripSearch } from '@/server/HAFAS/TripSearch/TripSearch'; import { vrrOccupancy } from '@/server/StopPlace/vrrOccupancy'; import { getOccupancy } from '@/server/coachSequence/occupancy'; -import { additionalJourneyInformation } from '@/server/journeys/additionalJourneyInformation'; import { rpcAppRouter, rpcProcedure } from '@/server/rpc/base'; -import type { AbfahrtenRPCQuery } from '@/server/rpc/iris'; -import { AllowedHafasProfile } from '@/types/HAFAS'; import type { RouteAuslastungWithSource } from '@/types/routing'; -import type { ArrivalStationBoardEntry } from '@/types/stationBoard'; import { TRPCError } from '@trpc/server'; import { z } from 'zod'; -const routingStopPlaceLocation = z.object({ - type: z.literal('stopPlace'), - evaNumber: z.string(), -}); - -const routingCoordinateLocation = z.object({ - type: z.literal('coordinate'), - latitude: z.number(), - longitude: z.number(), -}); - -const routingLocationInput = z.union([ - routingStopPlaceLocation, - routingCoordinateLocation, -]); - export const hafasRpcRouter = rpcAppRouter({ - irisAbfahrten: rpcProcedure - .input( - z.object({ - evaNumber: z.string(), - }), - ) - .query(async ({ input: { evaNumber } }) => { - const hafasDeparture = await StationBoard({ - type: 'DEP', - station: evaNumber, - }); - const hafasArrivals = await StationBoard({ - type: 'ARR', - station: evaNumber, - }).catch(() => undefined); - - const mappedHafasArrivals = - hafasArrivals?.reduce( - (map: Record, arrival) => { - map[`${arrival.jid}${arrival.train.number}`] = arrival; - - return map; - }, - {}, - ) || {}; - - const idSet = new Set(); - - return { - lookbehind: [], - departures: hafasDeparture - .map((departure) => - StationBoardToTimetables(departure, mappedHafasArrivals, idSet), - ) - .filter(Boolean) - .slice(0, 75), - wings: {}, - stopPlaces: [evaNumber], - }; - }) as AbfahrtenRPCQuery, occupancy: rpcProcedure .input( z.object({ @@ -131,65 +68,4 @@ export const hafasRpcRouter = rpcAppRouter({ }); }, ), - additionalInformation: rpcProcedure - .input( - z.object({ - trainName: z.string(), - journeyId: z.string(), - evaNumberAlongRoute: z.string().optional(), - initialDepartureDate: z.date().optional(), - }), - ) - .query( - async ({ - input: { - trainName, - journeyId, - evaNumberAlongRoute, - initialDepartureDate, - }, - }) => { - const additionalInformation = await additionalJourneyInformation( - trainName, - journeyId, - evaNumberAlongRoute, - initialDepartureDate, - ); - - if (additionalInformation) { - return additionalInformation; - } - - throw new TRPCError({ - code: 'NOT_FOUND', - }); - }, - ), - tripSearch: rpcProcedure - .input( - z.object({ - profile: z - .nativeEnum(AllowedHafasProfile) - .default(AllowedHafasProfile.DB), - start: routingLocationInput, - destination: routingLocationInput, - via: z.array( - z.object({ - evaNumber: z.string(), - minChangeTime: z.number().optional(), - }), - ), - time: z.date().optional(), - transferTime: z.number().optional(), - maxChanges: z.number().optional(), - searchForDeparture: z.boolean().optional(), - onlyRegional: z.boolean().optional(), - onlyNetzcard: z.boolean().optional(), - onlyBC100: z.boolean().optional(), - ctxScr: z.string().optional(), - }), - ) - .query(async ({ input: { profile, ...options } }) => { - return await tripSearch(options, profile); - }), }); diff --git a/src/server/rpc/journeys.ts b/src/server/rpc/journeys.ts index 4565072aa..e8f01319c 100644 --- a/src/server/rpc/journeys.ts +++ b/src/server/rpc/journeys.ts @@ -7,14 +7,11 @@ import { findJourneyHafasCompatible as findJourneyHafasCompatibleV2, findJourney as findJourneyV2, } from '@/external/risJourneysV2'; -import Detail from '@/server/HAFAS/Detail'; -import { enrichedJourneyMatch } from '@/server/HAFAS/JourneyMatch'; import { getCategoryAndNumberFromName } from '@/server/journeys/journeyDetails'; import { journeyDetails } from '@/server/journeys/v2/journeyDetails'; import { logger } from '@/server/logger'; import { rpcAppRouter, rpcProcedure } from '@/server/rpc/base'; -import type { ParsedJourneyMatchResponse } from '@/types/HAFAS/JourneyMatch'; -import type { ParsedSearchOnTripResponse } from '@/types/HAFAS/SearchOnTrip'; +import type { JourneyResponse } from '@/types/journey'; import { TRPCError } from '@trpc/server'; import type { QueryProcedure } from '@trpc/server/unstable-core-do-not-import'; import { isBefore, subDays } from 'date-fns'; @@ -75,7 +72,7 @@ export type JourneyRPCQuery = QueryProcedure<{ jid?: string; administration?: string; }; - output: ParsedSearchOnTripResponse | undefined | null; + output: JourneyResponse | undefined | null; }>; export const journeysRpcRouter = rpcAppRouter({ @@ -101,30 +98,13 @@ export const journeysRpcRouter = rpcAppRouter({ category, }, }) => { - let risPromise: Promise = Promise.resolve( - [], + let result = await findV1OrV2HafasCompatible( + trainNumber, + initialDepartureDate, + category, + withOEV, ); - if (trainNumber) { - risPromise = findV1OrV2HafasCompatible( - trainNumber, - initialDepartureDate, - category, - withOEV, - ); - } - - const trainName = trainNumber.toString(); - const hafasFallback = () => - enrichedJourneyMatch({ - withOEV, - trainName, - initialDepartureDate, - limit, - }); - - const risResult = await risPromise; - let result = risResult.length ? risResult : await hafasFallback(); if (initialEvaNumber) { result = result.filter( (r) => r.firstStop.station.evaNumber === initialEvaNumber, @@ -172,29 +152,13 @@ export const journeysRpcRouter = rpcAppRouter({ } return journey; } - const hafasFallback = async () => { - const hafasResult = await Detail( - trainName, - undefined, - evaNumberAlongRoute, - initialDepartureDate, - undefined, - undefined, - administration, - jid, - ); - if (!hafasResult) { - throw new TRPCError({ - code: 'NOT_FOUND', - }); - } - return hafasResult; - }; const productDetails = getCategoryAndNumberFromName(trainName); if (!productDetails) { - return hafasFallback(); + throw new TRPCError({ + code: 'NOT_FOUND', + }); } - let hafasResult: ParsedSearchOnTripResponse | undefined; + let hafasResult: JourneyResponse | undefined; if (jid && productDetails.trainNumber === 0) { // basierend auf Hafas versuchen, RIS::Journeys nur bei Bedarf hafasResult = await bahnJourneyDetails(jid); @@ -216,9 +180,11 @@ export const journeysRpcRouter = rpcAppRouter({ administration, ); if (!possibleJourneys.length) { - return hafasFallback(); + throw new TRPCError({ + code: 'NOT_FOUND', + }); } - let foundJourney: ParsedSearchOnTripResponse | undefined; + let foundJourney: JourneyResponse | undefined; if (evaNumberAlongRoute) { const allJourneys = ( @@ -235,7 +201,9 @@ export const journeysRpcRouter = rpcAppRouter({ foundJourney = await journeyDetails(possibleJourneys[0].journeyID); } if (!foundJourney) { - return hafasFallback(); + throw new TRPCError({ + code: 'NOT_FOUND', + }); } if (hafasResult) { diff --git a/src/server/sanitizeStorage.ts b/src/server/sanitizeStorage.ts index fc8cea6ee..7da4c87bd 100644 --- a/src/server/sanitizeStorage.ts +++ b/src/server/sanitizeStorage.ts @@ -2,21 +2,17 @@ import type { ServerStorage } from '@/client/Common/Storage'; import type { MinimalStopPlace } from '@/types/stopPlace'; export function sanitizeStorage(storage: ServerStorage): void { - sanitizeFavs(storage, 'favs'); - sanitizeFavs(storage, 'regionalFavs'); - sanitizeRoutingFavs(storage, 'rfavs'); + sanitizeFavs(storage); + sanitizeRoutingFavs(storage); } -function sanitizeFavs( - storage: ServerStorage, - storageKey: 'favs' | 'regionalFavs', -) { - const favs = storage.get(storageKey); +function sanitizeFavs(storage: ServerStorage) { + const favs = storage.get('favs'); if (!favs) { return; } if (typeof favs !== 'object') { - storage.remove(storageKey); + storage.remove('favs'); return; } let modified = false; @@ -26,23 +22,20 @@ function sanitizeFavs( modified = true; } } - if (modified) storage.set(storageKey, favs); + if (modified) storage.set('favs', favs); } function isCurrentFormatFav(stop?: MinimalStopPlace): boolean { return Boolean(stop?.evaNumber && stop.name); } -export function sanitizeRoutingFavs( - storage: ServerStorage, - storageKey: 'rfavs', -): void { - const favs = storage.get(storageKey); +export function sanitizeRoutingFavs(storage: ServerStorage): void { + const favs = storage.get('rfavs'); if (!favs) { return; } if (typeof favs !== 'object') { - storage.remove(storageKey); + storage.remove('rfavs'); return; } let modified = false; @@ -58,5 +51,5 @@ export function sanitizeRoutingFavs( modified = true; } } - if (modified) storage.set(storageKey, favs); + if (modified) storage.set('rfavs', favs); } diff --git a/src/swcWorkarounds/css.ts b/src/swcWorkarounds/css.ts deleted file mode 100644 index ca7d2cc05..000000000 --- a/src/swcWorkarounds/css.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { css } from '@mui/material/styles'; - -export default css; diff --git a/src/swcWorkarounds/keyframes.ts b/src/swcWorkarounds/keyframes.ts deleted file mode 100644 index 15ec6a382..000000000 --- a/src/swcWorkarounds/keyframes.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { keyframes } from '@mui/material/styles'; - -export default keyframes; diff --git a/src/swcWorkarounds/styled.ts b/src/swcWorkarounds/styled.ts deleted file mode 100644 index e6076f027..000000000 --- a/src/swcWorkarounds/styled.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { styled } from '@mui/material/styles'; - -export default styled; diff --git a/src/swcWorkarounds/useColorScheme.ts b/src/swcWorkarounds/useColorScheme.ts deleted file mode 100644 index b78b44732..000000000 --- a/src/swcWorkarounds/useColorScheme.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { useColorScheme } from '@mui/material/styles'; - -export default useColorScheme; diff --git a/src/swcWorkarounds/useTheme.ts b/src/swcWorkarounds/useTheme.ts deleted file mode 100644 index d1cb62346..000000000 --- a/src/swcWorkarounds/useTheme.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { useTheme } from '@mui/material/styles'; - -export default useTheme; diff --git a/src/types/.gitignore b/src/types/.gitignore deleted file mode 100644 index a6c7c2852..000000000 --- a/src/types/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/src/types/HAFAS/HimSearch.ts b/src/types/HAFAS/HimSearch.ts deleted file mode 100644 index 97458d850..000000000 --- a/src/types/HAFAS/HimSearch.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { - Common, - GenericHafasRequest, - HafasStation, - HimFilter, - OptionalLocL, - ParsedProduct, -} from '@/types/HAFAS'; - -export interface HimSearchRequestOptions { - comp?: string; - dailyB?: string; - dailyE?: string; - dateB?: string; - dateE?: string; - dept?: string; - dirLoc?: OptionalLocL; - himFltrL?: HimFilter[]; - maxNum?: number; - onlyHimId?: boolean; - onlyToday?: boolean; - stLoc?: OptionalLocL; - timeB?: string; - timeE?: string; -} -export interface HimSearchRequest - extends GenericHafasRequest<'HimSearch', HimSearchRequestOptions> {} - -export interface PubChL { - name: string; - fDate: string; - fTime: string; - tDate: string; - tTime: string; -} -export interface HimMessage { - hid: string; - act: boolean; - head: string; - lead: string; - text: string; - icoX: number; - prio: number; - fLocX: number; - tLocX: number; - prod: number; - affProdRefL: number[]; - IModDate: string; - IModTime: string; - sDate: string; - sTime: string; - eDate: string; - eTime: string; - comp: string; - cat: number; - pubChL: PubChL[]; - edgeRefL: number[]; -} - -export interface ParsedHimMessage extends HimMessage { - affectedProducts: ParsedProduct[]; - startTime: Date; - endTime: Date; - fromStopPlace?: HafasStation; - toStopPlace?: HafasStation; -} - -export interface HimSearchResponse { - common: Common; - msgL: HimMessage[]; -} - -export interface ParsedHimSearchResponse { - messages: ParsedHimMessage[]; -} diff --git a/src/types/HAFAS/JourneyDetails.ts b/src/types/HAFAS/JourneyDetails.ts deleted file mode 100644 index fa0ac0042..000000000 --- a/src/types/HAFAS/JourneyDetails.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { EvaNumber } from '@/types/common'; -import type { RouteAuslastung, RouteStop } from '@/types/routing'; -import type { - Common, - CommonStopInfo, - GenericHafasRequest, - Journey, - OptionalLocL, - ParsedPolyline, - ParsedProduct, - RemL, -} from '.'; - -// Additional Information we can only get from HAFAS in case of RIS Details. (Occupancy & correct operator names) -export interface AdditionalJourneyInformation { - jid?: string; - operatorName?: string; - occupancy: Record; - polyline?: ParsedPolyline; -} - -export interface JourneyDetailsResponse { - common: Common; - journey: Journey; - fpB: string; - fpE: string; - planrtTS: string; -} - -interface JourneyDetailsRequestReq { - jid: string; - getAltCoordinates?: boolean; - getAnnotations?: boolean; - getPasslist?: boolean; - getPolyline?: boolean; - getSimpleTrainComposition?: boolean; - getTrainComposition?: boolean; - - aDate?: string; - aIdx?: number; - aLoc?: OptionalLocL; - aTime?: string; - dDate?: string; - dIdx?: number; - dLoc?: OptionalLocL; - dTime?: string; - date?: string; - name?: string; - polySplitting?: boolean; -} -export interface JourneyDetailsRequest - extends GenericHafasRequest<'JourneyDetails', JourneyDetailsRequestReq> {} - -export interface RouteValidArrivalStop extends RouteStop { - arrival: CommonStopInfo; -} - -export interface RouteValidDepartureStop extends RouteStop { - departure: CommonStopInfo; -} - -export interface ParsedJourneyDetails { - train: ParsedProduct; - auslastung?: RouteAuslastung; - jid: string; - firstStop: RouteValidDepartureStop; - lastStop: RouteValidArrivalStop; - stops: RouteStop[]; - messages?: RemL[]; - polylines?: ParsedPolyline[]; -} diff --git a/src/types/HAFAS/JourneyMatch.ts b/src/types/HAFAS/JourneyMatch.ts deleted file mode 100644 index 8de84aacf..000000000 --- a/src/types/HAFAS/JourneyMatch.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { RouteStop } from '@/types/routing'; -import type { - Common, - GenericHafasRequest, - Journey, - JourneyFilter, - ParsedProduct, - RemL, -} from '.'; - -interface JounreyMatchRequestRes { - jnyFltrL?: JourneyFilter[]; - date: string; - dateB?: string; - dateE?: string; - extId?: string; - input: string; - - onlyCR?: boolean; - onlyRT?: boolean; - onlyTN?: boolean; - time?: string; - timeE?: string; - timeB?: string; - tripId?: string; - useAeqi?: boolean; -} -export interface JourneyMatchRequest - extends GenericHafasRequest<'JourneyMatch', JounreyMatchRequestRes> {} - -export interface JourneyMatchResponse { - common: Common; - jnyL: Journey[]; - fpB: string; - fpE: string; - planrtTS: string; -} - -export interface ParsedJourneyMatchResponse { - train: ParsedProduct; - stops: RouteStop[]; - jid: string; - firstStop: RouteStop; - lastStop: RouteStop; - messages?: RemL[]; -} -export interface JourneyMatchOptions { - /** - * Usually " " - * @example "ICE 23" - * @Example "Bus 94212" - */ - trainName: string; - /** - * Used to find the correct journey for a specific day. - * @default now - */ - initialDepartureDate?: Date; - /** - * These are raw HAFAS Filter and quite advanced. - */ - jnyFltrL?: JourneyFilter[]; - - // onlyRT?: boolean; -} - -export interface EnrichedJourneyMatchOptions extends JourneyMatchOptions { - limit?: number; - // Only FV, will also use "onlyRT" (unknown what the exact purpose is) - filtered?: boolean; -} diff --git a/src/types/HAFAS/LocMatch.ts b/src/types/HAFAS/LocMatch.ts deleted file mode 100644 index 5f8f45b58..000000000 --- a/src/types/HAFAS/LocMatch.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Common, GenericHafasRequest } from '.'; - -export interface LocMatchResponse { - common: Common; - match: { - field: string; - state: string; - locL: { - lid: string; - type: string; - name: string; - icoX: number; - extId: string; - state: string; - crd: { - x: number; - y: number; - layerX: number; - crdSysX: number; - z?: number; - }; - meta: boolean; - pCls: number; - pRefL: number[]; - wt: number; - }[]; - }; -} - -interface LocMatchRequestReq { - input: { - loc: { - name: string; - type: 'S' | 'ALL'; - }; - field: 'S'; - }; -} - -export interface LocMatchRequest - extends GenericHafasRequest<'LocMatch', LocMatchRequestReq> {} diff --git a/src/types/HAFAS/SearchOnTrip.ts b/src/types/HAFAS/SearchOnTrip.ts deleted file mode 100644 index c808afee7..000000000 --- a/src/types/HAFAS/SearchOnTrip.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { - TransportDestinationRef, - TransportOriginRef, -} from '@/external/generated/risJourneysV2'; -import type { MatchVehicleID } from '@/external/generated/risTransports'; -import type { HimIrisMessage } from '@/types/iris'; -import type { RouteJourneySegmentTrain, RouteStop } from '@/types/routing'; -import type { Common, GenericHafasRequest, ParsedPolyline } from '.'; -import type { OutConL, SotCtxt } from './TripSearch'; - -export interface SearchOnTripResponse { - common: Common; - fpB: string; - fpE: string; - bfATS: number; - bflOSTS: number; - planrtTS: number; - sotCtxt: SotCtxt; - outConL: OutConL[]; -} - -export type AllowedSotMode = 'JI' | 'RC'; -interface SearchOnTripJIDRequest { - jid: string; - sotMode: 'JI'; -} - -interface SearchOnTripCTXRequest { - ctxRecon: string; - sotMode: 'RC'; -} - -export interface SearchOnTripRequest - extends GenericHafasRequest< - 'SearchOnTrip', - SearchOnTripJIDRequest | SearchOnTripCTXRequest - > {} - -export interface ParsedSearchOnTripResponse extends RouteJourneySegmentTrain { - himMessages?: HimIrisMessage[]; - currentStop?: RouteStop; - polyline?: ParsedPolyline; - - continuationFor?: TransportOriginRef[]; - continuationBy?: TransportDestinationRef[]; - previousJourneys?: MatchVehicleID[]; - nextJourneys?: MatchVehicleID[]; -} diff --git a/src/types/HAFAS/StationBoard.ts b/src/types/HAFAS/StationBoard.ts deleted file mode 100644 index a15c2e496..000000000 --- a/src/types/HAFAS/StationBoard.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { - Common, - CommonArrival, - CommonDeparture, - CommonJny, - GenericHafasRequest, - JourneyFilter, - OptionalLocL, -} from '.'; - -export enum StationBoardSortType { - EVAID = 'EVAID', - PT = 'PT', - RT = 'RT', -} - -interface StationBoardRequestReq { - type: 'DEP' | 'ARR'; - date: string; - dateB?: string; - dateE?: string; - dur?: number; - getPasslist?: boolean; - getSimpleTrainComposition?: boolean; - getTrainComposition?: boolean; - time: string; - stbLoc: OptionalLocL; - dirLoc?: OptionalLocL; - jnyFltrL?: JourneyFilter[]; - locFltrL?: unknown[]; - maxJny?: number; - minDur?: number; - per?: boolean; - qrCode?: string; - sort?: StationBoardSortType; - stbFltrEquiv?: boolean; -} - -export interface StationBoardRequest - extends GenericHafasRequest<'StationBoard', StationBoardRequestReq> {} -export interface CommonStationBoardResponse { - common: Common; - fpB: string; - fpE: string; - planrtTS: string; - sD: string; - sT: string; - locRefL: number[]; -} -export interface ArrivalStationBoardResponse - extends CommonStationBoardResponse { - type: 'ARR'; - jnyL: ArrivalJny[]; -} -export interface DepartureStationBoardResponse - extends CommonStationBoardResponse { - type: 'DEP'; - jnyL: DepartureJny[]; -} -export type StationBoardResponse = - | ArrivalStationBoardResponse - | DepartureStationBoardResponse; - -export type DepStbStop = CommonDeparture; - -export type ArrStbStop = CommonArrival; - -export interface ArrivalJny extends CommonJny { - date: string; - stbStop: ArrStbStop; -} -export interface DepartureJny extends CommonJny { - date: string; - stbStop: DepStbStop; -} - -export type StationBoardJny = ArrivalJny | DepartureJny; diff --git a/src/types/HAFAS/Tarif.ts b/src/types/HAFAS/Tarif.ts deleted file mode 100644 index c10ab4a56..000000000 --- a/src/types/HAFAS/Tarif.ts +++ /dev/null @@ -1,26 +0,0 @@ -export enum JnyCl { - first = 1, - second = 2, -} - -export enum LoyalityCard { - BC25First = 'BC25First', - BC25Second = 'BC25Second', - BC50First = 'BC50First', - BC50Second = 'BC50Second', - SHCard = 'SHCard', - ATVorteilscard = 'ATVorteilscard', - CHGeneral = 'CHGeneral', - CHHalfWithRailplus = 'CHHalfWithRailplus', - CHHalfWithoutRailplus = 'CHHalfWithoutRailplus', - NLWithRailplus = 'NLWithRailplus', - NLWithoutRailplus = 'NLWithoutRailplus', - first100 = 'BC100First', - second100 = 'BC100Second', -} - -export enum TravelerType { - Adult = 'E', - Kid = 'K', - Baby = 'B', -} diff --git a/src/types/HAFAS/TripSearch.ts b/src/types/HAFAS/TripSearch.ts deleted file mode 100644 index 604e8068a..000000000 --- a/src/types/HAFAS/TripSearch.ts +++ /dev/null @@ -1,328 +0,0 @@ -import type { Coordinate2D } from '@/external/types'; -import type { JnyCl, LoyalityCard, TravelerType } from '@/types/HAFAS/Tarif'; -import type { EvaNumber } from '@/types/common'; -import type { - Common, - CommonArrival, - CommonDeparture, - CommonJny, - CommonStop, - GenericHafasRequest, - JourneyFilter, - MsgL, - OptionalLocL, - TrnCmpSX, -} from '.'; - -export interface TripSearchTraveler { - type: TravelerType; - /** - * Testing Comment - */ - loyalityCard?: LoyalityCard; -} - -export interface TripSearchTarifRequest { - class: JnyCl; - traveler: TripSearchTraveler[]; -} - -interface BaseTripSearchOptions extends SharedTripSearchOptions { - time?: Date; - - transferTime?: number; - maxChanges?: number; - searchForDeparture?: boolean; - onlyRegional?: boolean; - // Experimental filter to only use stuff that Netzcard allows. Use at own risk! - onlyNetzcard?: boolean; - // Filters out Attribute "DU" - "DB Fahrsscheine gelten nicht" - onlyBC100?: boolean; - tarif?: TripSearchTarifRequest; -} - -interface TripSearchViaV3 { - evaId: string; - minChangeTime?: number; -} - -export interface TripSearchOptionsV3 extends BaseTripSearchOptions { - start: EvaNumber; - destination: EvaNumber; - via?: TripSearchViaV3[]; -} - -interface StopPlaceLocation { - type: 'stopPlace'; - evaNumber: EvaNumber; -} - -interface CoordinateLocation extends Coordinate2D { - type: 'coordinate'; -} - -interface TripSearchViaV4 { - evaNumber: string; - minChangeTime?: number; -} - -type RoutingLocationInput = StopPlaceLocation | CoordinateLocation; - -export interface TripSearchOptionsV4 extends BaseTripSearchOptions { - start: RoutingLocationInput; - destination: RoutingLocationInput; - via?: TripSearchViaV4[]; -} - -interface SharedTripSearchOptions { - /** - * true = not only fastest route - */ - economic?: boolean; - /** - * Unknown flag - */ - getIV?: boolean; - /** - * Get Stop inbetween - */ - getPasslist?: boolean; - /** - * Polylines - unknown format - */ - getPolyline?: boolean; - numF?: number; - ctxScr?: string; - /** - * Is a station nearby enough for routing? - */ - ushrp?: boolean; -} - -export interface TravelerProfile { - type: TravelerType; - redtnCard?: number; -} - -export interface TarifRequest { - jnyCl: JnyCl; - cType: 'PK'; - tvlrProf: TravelerProfile[]; -} - -interface GenericTripSearchRequest extends SharedTripSearchOptions { - arrLocL: OptionalLocL[]; - depLocL: OptionalLocL[]; - viaLocL: { - loc: OptionalLocL; - min?: number; - }[]; - antiViaLocL?: { - loc: OptionalLocL; - }[]; - getPT: boolean; - maxChg: number; - minChgTime: number; - outFrwd: boolean; - jnyFltrL?: JourneyFilter[]; - trfReq?: TarifRequest; - - baim?: boolean; - ctxScr?: string; - getConGroups?: boolean; - numB?: number; - outReconL?: unknown[]; - retDate?: string; - retReconL?: unknown[]; - retTime?: string; - extChgTime?: number; - getAltCoordinates?: boolean; - getAnnotations?: boolean; - getEco?: boolean; - getEcoCmp?: boolean; - getIST?: boolean; - liveSearch?: boolean; - prefLocL?: { - loc: OptionalLocL; - }[]; - pt?: string; -} -interface DateTimeTripSeachRequest extends GenericTripSearchRequest { - outDate: string; - outTime: string; -} -interface AfterBeforeTripSearchRequest extends GenericTripSearchRequest { - ctxScr: string; -} -export interface TripSearchRequest - extends GenericHafasRequest< - 'TripSearch', - DateTimeTripSeachRequest | AfterBeforeTripSearchRequest - > { - cfg?: { - rtMode: 'HYBRID'; - }; -} - -export interface SDays { - sDaysR: string; - sDaysI: string; - sDaysB: string; -} - -export interface JnyL extends CommonJny { - stopL: CommonStop[]; -} - -export interface Freq { - minC: number; - maxC: number; - numC: number; - jnyL: JnyL[]; -} - -export interface Jny extends CommonJny { - chgDurR?: number; - isCncl?: boolean; - stopL?: CommonStop[]; - ctxRecon: string; - dTrnCmpSXmsgL: MsgL[]; - dTrnCmpSX?: TrnCmpSX; - freq: Freq; - msgL?: MsgL[]; -} - -export interface SecLJNY { - type: 'JNY'; - icoX: number; - dep: CommonDeparture; - arr: CommonArrival; - jny: Jny; - parJnyL?: Jny[]; - resState: 'N' | 'B' | 'S'; - resRecommendation: string; -} - -export interface Gis { - dist: number; - durS: string; - dirGeo: number; - ctx: string; - gisPrvr: string; - getDescr: boolean; - getPoly: boolean; -} -export interface SecLWALK { - type: 'WALK' | 'TRSF'; - icoX: number; - dep: CommonDeparture; - arr: CommonArrival; - gis: Gis; -} - -export interface SecLKISS { - type: 'KISS'; -} - -export type SecL = SecLJNY | SecLWALK | SecLKISS; - -export interface SotCtxt { - cnLocX: number; - calcDate: string; - jid: string; - locMode: string; - pLocX: number; - reqMode: string; - sectX: number; - calcTime: string; -} - -export interface TarifFare { - /** - * In Cent - */ - prc: number; - /** - * Does a more expensive Tarif exist? - */ - isFromPrice: boolean; - /** - * Can you you still buy this? - */ - isBookable: boolean; - /** - * ??? - */ - isUpsell: boolean; - /** - * ??? - */ - targetCtx: string; - buttonText: string; -} -export interface TarifFareSet { - fareL: TarifFare[]; -} -export interface HafasTarifResponse { - statusCode: 'OK' | Omit; - fareSetL: TarifFareSet[]; -} - -export interface OutConL { - isNotRdbl?: boolean; - cid: string; - date: string; - dur: string; - chg: number; - sDays: SDays; - dep: CommonDeparture; - arr: CommonArrival; - secL: SecL[]; - ctxRecon: string; - trfRes?: HafasTarifResponse; - conSubscr: string; - resState: string; - resRecommendation: string; - recState: string; - sotRating: number; - isSotCon: boolean; - showARSLink: boolean; - sotCtxt: SotCtxt; - cksum: string; - cksumDti: string; - msgL: MsgL[]; - dTrnCmpSX: TrnCmpSX; - freq: Freq; - isAlt?: boolean; -} - -export interface ConScoreL { - score: number; - conRefL: number[]; -} - -export interface ConScoringL { - type: string; - conScoreL: ConScoreL[]; -} - -export interface OutConGrpL { - name: string; - icoX: number; - grpid: string; - conScoringL: ConScoringL[]; - initScoringType: string; -} - -export interface TripSearchResponse { - common: Common; - outConL: OutConL[]; - outCtxScrB: string; - outCtxScrF: string; - fpB: string; - fpE: string; - bfATS: number; - bfIOSTS: number; - planrtTS: string; - outConGrpL: OutConGrpL[]; -} diff --git a/src/types/HAFAS/deprecated/JourneyCourse.ts b/src/types/HAFAS/deprecated/JourneyCourse.ts deleted file mode 100644 index 89231b132..000000000 --- a/src/types/HAFAS/deprecated/JourneyCourse.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { - Common, - GeoRect, - HafasDirection, - JourneyFilter, - OptionalLocL, - ParsedPolyline, -} from '@/types/HAFAS'; - -export interface JourneyCourseRequestOptions { - arrLoc?: OptionalLocL; - /** - * yyyyMMdd - */ - date: string; - depLoc?: OptionalLocL; - dir?: HafasDirection; - getEdgeAni?: boolean; - getEdgeCourse?: boolean; - getIST?: boolean; - getMainAni?: boolean; - getMainCourse?: boolean; - getPassLoc?: boolean; - getPolyline?: boolean; - jid: string; - jnyFltrL?: JourneyFilter[]; - perSize?: number; - perStep?: number; - /** - * HHmm - */ - time?: string; -} - -export interface JourneyCourseRequest { - req: JourneyCourseRequestOptions; - meth: 'JourneyCourse'; -} - -export interface JourneyCourseResponse { - common: Common; - date: string; - time: string; - mainPoly: { - polyXL: number[]; - }; - rect: GeoRect; - layerX: number; - crdSysX: number; -} - -export interface ParsedJourneyCourseResponse { - polylines: ParsedPolyline[]; -} diff --git a/src/types/HAFAS/deprecated/JourneyGraph.ts b/src/types/HAFAS/deprecated/JourneyGraph.ts deleted file mode 100644 index d718fe2e1..000000000 --- a/src/types/HAFAS/deprecated/JourneyGraph.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { JourneyFilter } from '@/types/HAFAS'; - -export interface JourneyGraphRequestOptions { - /** - * yyyyMMdd - */ - date?: string; - getPasslist?: boolean; - getProductStartEndInfo?: boolean; - jnyFltrL?: JourneyFilter[]; -} - -export interface JourneyGraphRequest { - req: JourneyGraphRequestOptions; - meth: 'JourneyGraph'; -} - -export type JourneyGraphResponse = any; diff --git a/src/types/HAFAS/deprecated/JourneyTree.ts b/src/types/HAFAS/deprecated/JourneyTree.ts deleted file mode 100644 index d6418ce24..000000000 --- a/src/types/HAFAS/deprecated/JourneyTree.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { - GenericHafasRequest, - GeoRect, - GeoRing, - HimFilter, - JourneyFilter, -} from '@/types/HAFAS'; - -export interface JourneyTreeRequestOptions { - getChilds?: number; - getHIM?: boolean; - getParent?: boolean; - getStatus?: boolean; - himFltrL?: HimFilter[]; - jnyFltrL?: JourneyFilter[]; - pid?: string; - rect?: GeoRect; - ring?: GeoRing; -} - -export interface JourneyTreeRequest extends GenericHafasRequest<'JourneyTree'> { - req: JourneyTreeRequestOptions; -} - -export type JourneyTreeResponse = unknown; diff --git a/src/types/HAFAS/index.ts b/src/types/HAFAS/index.ts deleted file mode 100644 index 871b92fae..000000000 --- a/src/types/HAFAS/index.ts +++ /dev/null @@ -1,562 +0,0 @@ -import type { HimSearchRequest } from '@/types/HAFAS/HimSearch'; -import type { JourneyMatchRequest } from '@/types/HAFAS/JourneyMatch'; -import type { SearchOnTripRequest } from '@/types/HAFAS/SearchOnTrip'; -import type { StationBoardRequest } from '@/types/HAFAS/StationBoard'; -import type { JourneyTreeRequest } from '@/types/HAFAS/deprecated/JourneyTree'; -import type { MinimalStopPlace } from '@/types/stopPlace'; -import type { JourneyDetailsRequest } from './JourneyDetails'; -import type { LocMatchRequest } from './LocMatch'; -import type { TripSearchRequest } from './TripSearch'; - -export type JourneyFilterMode = 'BIT' | 'EXC' | 'INC' | 'UNDEF'; -export type JourneyFilterType = - | 'ADM' - | 'ATTRF' - | 'ATTRJ' - | 'ATTRL' - | 'BC' - | 'CAT' - | 'COUCH' - | 'CTX_RECON' - | 'GROUP' - | 'INFOTEXTS' - | 'JID' - | 'LID' - | 'LINE' - | 'LINEID' - | 'META' - | 'NAME' - | 'NUM' - | 'OP' - | 'PID' - | 'PROD' - | 'ROUTE' - | 'SLEEP' - | 'STATIONS' - | 'UIC'; - -export type AllowedHafasMethods = - | 'BookingAssortment' - | 'BookingData' - | 'BookingValidation' - | 'FeederBoard' - | 'FetcherBoard' - | 'GisRoute' - | 'HimMatch' - | 'HimSearch' - | 'JourneyCourse' - | 'JourneyDetails' - | 'JourneyGeoPos' - | 'JourneyGraph' - | 'JourneyMatch' - | 'JourneyTree' - | 'LocDetails' - | 'LocGeoPos' - | 'LocGeoReach' - | 'LocGraph' - | 'LocMatch' - | 'MatchMe' - | 'OneFieldSearch' - | 'PartialSearch' - | 'Reconstruction' - | 'SearchOnTrip' - | 'ServerInfo' - | 'StationBoard' - | 'StationDetails' - | 'SubscriptionCreate' - | 'SubscriptionDelete' - | 'SubscriptionDetails' - | 'SubscriptionNotification' - | 'SubscriptionSearch' - | 'SubscriptionStatus' - | 'SubscriptionUpdate' - | 'SubscriptionUserCreate' - | 'SubscriptionUserDelete' - | 'SubscriptionUserUpdate' - | 'SubscriptionValidate' - | 'TripSearch'; - -export type HafasDirection = 'B' | 'F' | 'FB'; -export type HimFilterMode = 'BIT' | 'EXC' | 'INC' | 'UNDEF'; -export type HimFilterType = - | 'ADMIN' - | 'CAT' - | 'CH' - | 'COMP' - | 'DEPT' - | 'EID' - | 'HIMCAT' - | 'HIMID' - | 'LINE' - | 'OPR' - | 'PID' - | 'PROD' - | 'REG' - | 'TRAIN'; -export interface HimFilter { - mode: HimFilterMode; - type: HimFilterType; - value: string; -} -export interface JourneyFilter { - mode: JourneyFilterMode; - type: JourneyFilterType; - value: string; -} - -export type LocationFilterMode = 'BIT' | 'EXC' | 'INC'; -export type LocationFilterType = - | 'ATTRL' - | 'ATTRP' - | 'META' - | 'NGR' - | 'PLATF' - | 'PROD' - | 'ROUP_A' - | 'ROUP_N' - | 'ROUP_S' - | 'ROUP_V' - | 'ROUP_Z' - | 'ROUS_A' - | 'ROUS_N' - | 'ROUS_S' - | 'ROUS_V' - | 'ROUS_Z' - | 'ROU_A' - | 'ROU_N' - | 'ROU_S' - | 'ROU_Z' - | 'SLCTP_A' - | 'SLCTP_N' - | 'SLCTP_V' - | 'SLCTP_Z' - | 'SLCTS_A' - | 'SLCTS_N' - | 'SLCTS_S' - | 'SLCTS_V' - | 'SLCTS_Z' - | 'SLCT_A' - | 'SLCT_N' - | 'SLCT_S' - | 'SLCT_V' - | 'SLCVT_Z'; - -export type LocationNGrammFilterMode = - | 'DIST_ATTR' - | 'DIST_INFO' - | 'DIST_PERI' - | 'DIST_RNG' - | 'DIST_STNR' - | 'EXCL_ATTR' - | 'EXCL_INFO' - | 'EXCL_META' - | 'EXCL_PERI' - | 'EXCL_RNG' - | 'EXCL_STNR' - | 'ONLY_META' - | 'SLCT_ATTR' - | 'SLCT_INFO' - | 'SLCT_PERI' - | 'SLCT_PROD' - | 'SLCT_RNG' - | 'SLCT_STNR'; -export interface LocationNGrammFilter { - attr?: string; - crd?: Crd; - endIds?: string; - fTxt?: string; - maxDist?: number; - startIds?: string; - type: LocationNGrammFilterMode; -} -export interface LocationFilter { - mode: LocationFilterMode; - ngramm: LocationNGrammFilter; - type: LocationFilterType; - value: string; -} - -export interface CommonProductInfo { - name: string; - line?: string; - number?: string; - /** - * This is actually category - */ - type?: string; - operator?: OpL; - admin?: string; - // was TRANSPORT_TYPE ENUM before - transportType: string; -} -export interface CommonStopInfo { - /** - * Quelle dieser info ist die Planwagenreihung - */ - isPlan?: boolean; - /** - * Scheduled Platform - */ - scheduledPlatform?: string; - /** - * Best known platform, might be identical to scheduledPlatform - */ - platform?: string; - /** - * scheduled time for this stop - */ - scheduledTime: Date; - /** - * best known time for this stop, might be identical to scheduledTime - */ - time: Date; - /** - * @isInt - */ - delay?: number; - messages?: RemL[]; - cancelled?: boolean; - additional?: boolean; - noPassengerChange?: boolean; - /** - * Arrival/Departure ID - */ - id?: string; - /** REPORTED Time */ - isRealTime?: boolean; -} - -export interface RemL { - type: string; - code: string; - icoX: number; - txtN: string; - txtS?: string; - prio?: number; - sIdx?: number; -} - -export interface SDaysL { - sDaysR: string; - sDaysI: string; - sDaysB: string; - fLocX: number; - tLocX: number; -} - -export interface HafasCoordinates { - lat: number; - lng: number; -} - -export interface HafasStation extends MinimalStopPlace { - products?: ParsedProduct[]; - coordinates: HafasCoordinates; -} - -export enum AllowedHafasProfile { - DB = 'db', - OEBB = 'oebb', - BVG = 'bvg', - HVV = 'hvv', - RMV = 'rmv', - SNCB = 'sncb', - AVV = 'avv', - 'NAH.SH' = 'nahsh', - INSA = 'insa', - aNachB = 'anachb', - VAO = 'vao', - SBB = 'sbb', - DBNetz = 'dbnetz', - PKP = 'pkp', - DBRegio = 'dbregio', - DBSmartRBL = 'smartrbl', - VBN = 'vbn', - // Not a real hafas profile, used in routing for bahn.de stuff - BAHN = 'bahn', - // all = 'all', -} - -export interface GenericHafasRequest< - out Meth extends string, - out Req = unknown, -> { - meth: Meth; - req: Req; -} -export type HafasRequest = SingleHafasRequest[]; -export type SingleHafasRequest = - // | JourneyCourseRequest - // | JourneyGraphRequest - | JourneyTreeRequest - | StationBoardRequest - | HimSearchRequest - | JourneyMatchRequest - | LocMatchRequest - | JourneyDetailsRequest - | SearchOnTripRequest - | TripSearchRequest; - -interface CInfo { - code: string; - url: string; - msg: string; -} - -interface SvcResL { - meth: string; - err: string; - res: Res; -} - -export interface GenericRes { - common: Common; -} - -export interface HafasResponse { - ver: string; - lang: string; - id: string; - err: string; - cInfo: CInfo; - svcResL: SvcResL[]; -} - -export interface ProdCtx { - name: string; - num?: string; - matchId?: string; - catOut?: string; - catOutS?: string; - catOutL?: string; - catIn?: string; - catCode?: string; - admin?: string; - lineId?: string; - line?: string; - cls: number; - icoX: number; -} - -export interface ProdL { - name: string; - number?: string; - icoX: number; - cls: number; - oprX?: number; - prodCtx?: ProdCtx; - addName?: string; - nameS?: string; - matchId?: string; -} - -export interface LayerL { - id: string; - name: string; - index: number; - annoCnt: number; -} - -export interface CrdSysL { - id: string; - index: number; - type: string; -} - -export interface IcoL { - res: string; - txt?: string; -} - -export interface Crd { - x: number; - y: number; - z?: number; - layerX?: number; - crdSysX?: number; -} - -export interface PolyG { - polyXL: number[]; - layerX: number; - crdSysX: number; -} - -export interface Journey { - jid: string; - date: string; - prodX: number; - status?: string; - isRchbl?: boolean; - stopL: CommonStop[]; - sDaysL: SDaysL[]; - polyG?: PolyG; - msgL?: MsgL[]; - subscr?: string; - prodL?: ProdL[]; - dTrnCmpSX?: TrnCmpSX; -} -export interface OptionalLocL { - lid?: string; - type?: string; - name?: string; - icoX?: number; - extId?: string; - state?: string; - crd?: Crd; - pCls?: number; - /** - * Reference to prodL - */ - pRefL?: number[]; -} - -export type LocL = Required; - -export interface PpLocRefL { - ppIdx: number; - locX: number; -} - -export interface PolyL { - delta: boolean; - dim: number; - crdEncYX: string; - crdEncS: string; - crdEncF: string; - ppLocRefL: PpLocRefL[]; -} - -export interface OpL { - name: string; - icoX?: number; -} - -export interface TcocL { - c: string; - r?: number; -} - -export interface HimMsgEdgeL { - icoCrd: { - x: string; - y: string; - }; -} - -export interface Common { - locL: LocL[]; - prodL: ProdL[]; - polyL: PolyL[]; - layerL: LayerL[]; - crdSysL: CrdSysL[]; - opL: OpL[]; - remL: RemL[]; - icoL: IcoL[]; - tcocL?: TcocL[]; - himMsgEdgeL?: HimMsgEdgeL[]; -} - -export interface CommonJny { - jid: string; - prodX: number; - dirTxt: string; - status: string; - isRchbl: boolean; - isCncl?: boolean; - isPartCncl?: boolean; - subscr: string; - stopL?: CommonStop[]; - msgL?: MsgL[]; -} - -export interface CommonArrival { - locX: number; - idx?: number; - aCncl?: boolean; - aProdX?: number; - aOutR: boolean; - aTimeS: string; - aTimeR?: string; - aPlatfS?: string; - aPlatfR?: string; - aProgType?: string; - type?: string; - aTZOffset?: number; - aTrnCmpSX?: TrnCmpSX; - msgL?: MsgL[]; -} - -export interface CommonDeparture { - locX: number; - idx?: number; - dCncl?: boolean; - dProdX?: number; - dInS: boolean; - dInR: boolean; - dTimeS: string; - dTimeR?: string; - dPlatfS?: string; - dPlatfR?: string; - dProgType?: string; - type?: string; - dTZOffset?: number; - dTrnCmpSX?: TrnCmpSX; - msgL?: MsgL[]; -} - -export interface CommonStop extends CommonArrival, CommonDeparture { - isAdd?: boolean; -} - -export interface TxtC { - r: number; - g: number; - b: number; -} - -export interface MsgL { - type: string; - remX: number; - txtC: TxtC; - prio: number; - fIdx: number; - tIdx: number; - tagL: string[]; -} - -export interface TrnCmpSX { - tcocX?: number[]; - tcM?: number; -} - -// ParsedStuff -interface _ParsedCommon { - locL: HafasStation[]; - prodL: ParsedProduct[]; - polyL?: ParsedPolyline[]; -} -export type ParsedCommon = _ParsedCommon & - Omit; - -export type ParsedProduct = CommonProductInfo; - -export interface ParsedPolyline { - // Actually [number, number][] - points: number[][]; - delta?: boolean; - locations: HafasStation[]; -} - -export type InOutMode = 'B' | 'I' | 'N' | 'O'; - -export interface GeoRect { - llCrd: Crd; - urCrd: Crd; -} - -export interface GeoRing { - cCrd: Crd; - maxDist: number; - minDist?: number; -} diff --git a/src/types/common.ts b/src/types/common.ts deleted file mode 100644 index 40dc4be3c..000000000 --- a/src/types/common.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Usually 7 digits, leading zeros can be omitted - * @example "8000105" - * @example "100010" - */ -export type EvaNumber = string; diff --git a/src/types/iris.ts b/src/types/iris.ts index 5f68f95cc..429bf095c 100644 --- a/src/types/iris.ts +++ b/src/types/iris.ts @@ -1,5 +1,5 @@ -import type { CommonProductInfo, CommonStopInfo } from '@/types/HAFAS'; -import type { MinimalStopPlace } from '@/types/stopPlace'; +import type { CommonProductInfo } from '@/types/journey'; +import type { CommonStopInfo, MinimalStopPlace } from '@/types/stopPlace'; export interface IrisStationWithRelated { station: IrisStation; diff --git a/src/types/journey.ts b/src/types/journey.ts new file mode 100644 index 000000000..05b288786 --- /dev/null +++ b/src/types/journey.ts @@ -0,0 +1,37 @@ +import type { + TransportDestinationRef, + TransportOriginRef, +} from '@/external/generated/risJourneysV2'; +import type { MatchVehicleID } from '@/external/generated/risTransports'; +import type { RouteJourneySegmentTrain, RouteStop } from '@/types/routing'; + +export interface JourneyResponse extends RouteJourneySegmentTrain { + currentStop?: RouteStop; + + continuationFor?: TransportOriginRef[]; + continuationBy?: TransportDestinationRef[]; + previousJourneys?: MatchVehicleID[]; + nextJourneys?: MatchVehicleID[]; +} + +export interface JourneyFindResponse { + train: CommonProductInfo; + stops: RouteStop[]; + jid: string; + firstStop: RouteStop; + lastStop: RouteStop; +} + +export interface CommonProductInfo { + name: string; + line?: string; + number?: string; + /** + * This is actually category + */ + type?: string; + operator?: string; + admin?: string; + // was TRANSPORT_TYPE ENUM before + transportType: string; +} diff --git a/src/types/reihung.ts b/src/types/reihung.ts deleted file mode 100644 index dac98cd1b..000000000 --- a/src/types/reihung.ts +++ /dev/null @@ -1,202 +0,0 @@ -import type { AvailableBR, AvailableIdentifier } from '@/types/coachSequence'; - -interface Auslastungsstufe { - auslastungsstufeErsteKlasse: string; - auslastungsstufeZweiteKlasse: string; -} - -export interface BaseFormation { - /** - * Should always be VORWAERTS - */ - fahrtrichtung: 'VORWAERTS' | 'RUCKWAERTS'; - allFahrzeuggruppe: BaseFahrzeuggruppe[]; - halt: Halt; - liniebezeichnung: string; - zuggattung: string; - zugnummer: string; - /** - * ??? - */ - serviceid: string; - planstarttag: string; - /** - * ??? - */ - fahrtid: string; - istplaninformation: boolean; - auslastungsstufe?: Auslastungsstufe; -} - -export interface Formation extends BaseFormation { - allFahrzeuggruppe: Fahrzeuggruppe[]; - /** - * Train Category that was reported - */ - reportedZuggattung: string; - /** - * do groups have a different destination - */ - differentDestination?: boolean; - /** - * Do we have several trains in one? (Flügelung) - */ - differentZugnummer?: boolean; - scale: number; - startPercentage: number; - endPercentage: number; - /** - * true = Vorwärts, undefined = we do not know - */ - realFahrtrichtung?: boolean; - isRealtime: boolean; -} - -export interface BaseFahrzeuggruppe { - allFahrzeug: BaseFahrzeug[]; - fahrzeuggruppebezeichnung: string; - zielbetriebsstellename: string; - startbetriebsstellename: string; - verkehrlichezugnummer: string; -} - -export interface Fahrzeuggruppe extends BaseFahrzeuggruppe { - goesToFrance: boolean; - allFahrzeug: Fahrzeug[]; - startPercentage: number; - endPercentage: number; - /** - * 0169 for instance, from Gruppenbezeichnung - */ - tzn?: string; - /** - * Worms for instance - */ - name?: string; - br?: BRInfo; -} - -export interface Halt { - abfahrtszeit?: string; // date-time - - ankunftszeit?: string; // date-time - - bahnhofsname: string; - evanummer: string; - gleisbezeichnung?: string; - haltid: string; - rl100: string; - allSektor: Sektor[]; -} - -export interface BRInfo { - name: string; - BR?: AvailableBR; - identifier?: AvailableIdentifier; - noPdf?: boolean; - country?: 'DE' | 'AT'; - showBRInfo?: boolean; -} - -export type FahrzeugKategorie = - | 'DOPPELSTOCKSTEUERWAGENERSTEKLASSE' - | 'DOPPELSTOCKSTEUERWAGENERSTEZWEITEKLASSE' - | 'DOPPELSTOCKSTEUERWAGENZWEITEKLASSE' - | 'DOPPELSTOCKWAGENERSTEKLASSE' - | 'DOPPELSTOCKWAGENERSTEZWEITEKLASSE' - | 'DOPPELSTOCKWAGENZWEITEKLASSE' - | 'HALBSPEISEWAGENERSTEKLASSE' - | 'HALBSPEISEWAGENZWEITEKLASSE' - | 'LOK' - | 'REISEZUGWAGENERSTEKLASSE' - | 'REISEZUGWAGENERSTEZWEITEKLASSE' - | 'REISEZUGWAGENZWEITEKLASSE' - | 'SPEISEWAGEN' - | 'STEUERWAGENERSTEKLASSE' - | 'STEUERWAGENERSTEZWEITEKLASSE' - | 'STEUERWAGENZWEITEKLASSE' - | 'TRIEBKOPF' - | ''; - -export interface BaseFahrzeug { - allFahrzeugausstattung: Fahrzeugausstattung[]; - kategorie: FahrzeugKategorie; - fahrzeugnummer: string; - orientierung: string; - positioningruppe: string; - fahrzeugsektor: string; - fahrzeugtyp: string; - wagenordnungsnummer: string; - positionamhalt: Position; - status: 'GESCHLOSSEN' | Omit; -} - -export interface Fahrzeug extends BaseFahrzeug { - additionalInfo: AdditionalFahrzeugInfo; -} -export interface Position { - endemeter: string; - endeprozent: string; - startmeter: string; - startprozent: string; -} - -export interface Sektor { - positionamgleis: Position; - sektorbezeichnung: string; -} - -export interface SpecialSeats { - comfort?: number[]; - express?: number[]; - disabled?: number[]; -} - -export interface AdditionalFahrzeugInfo { - /** - * 0: Unknown; 1: erste; 2: zweite; 3: 1&2; 4: Not for passengers - */ - klasse: 0 | 1 | 2 | 3 | 4; - icons: { - dining?: boolean; - wheelchair?: boolean; - bike?: boolean; - disabled?: boolean; - quiet?: boolean; - info?: boolean; - family?: boolean; - toddler?: boolean; - wifi?: boolean; - }; - comfort?: boolean; - comfortSeats?: string; - disabledSeats?: string; - familySeats?: string; -} - -export interface Fahrzeugausstattung { - anzahl: string; - ausstattungsart: string; - bezeichnung: string; - status: string; -} - -export interface Wagenreihung { - meta: Meta; - data: Data; - // only for DB-newApps - journeyID?: string; -} - -export interface Meta { - id: string; - owner: string; - format: 'JSON'; - version: string; - created: string; - sequence: number; -} - -export interface Data { - istformation: BaseFormation; -} diff --git a/src/types/routing.ts b/src/types/routing.ts index 3a3fdd778..72159cc0c 100644 --- a/src/types/routing.ts +++ b/src/types/routing.ts @@ -4,15 +4,12 @@ import type { TransportDestinationRef, TransportWithDirection, } from '@/external/generated/risJourneysV2'; -import type { MinimalStopPlace } from '@/types/stopPlace'; +import type { CommonProductInfo } from '@/types/journey'; import type { CommonStopInfo, - HafasStation, - ParsedProduct, - ProdL, - RemL, -} from './HAFAS'; -import type { SecL } from './HAFAS/TripSearch'; + GroupedStopPlace, + MinimalStopPlace, +} from '@/types/stopPlace'; import type { Message } from './iris'; export interface RouteStop { @@ -20,7 +17,6 @@ export interface RouteStop { departure?: CommonStopInfo; station: MinimalStopPlace; auslastung?: RouteAuslastung; - messages?: RemL[]; additional?: boolean; cancelled?: boolean; irisMessages?: Message[]; @@ -64,14 +60,12 @@ export interface RouteJourney { jid?: string; // RIS JourneyID journeyId?: string; - product?: ProdL; - raw?: SecL; segmentDestination: MinimalStopPlace; segmentStart: MinimalStopPlace; stops: RouteStop[]; - train: ParsedProduct; + train: CommonProductInfo; auslastung?: RouteAuslastung; - messages?: RemL[]; + messages?: string[]; tarifSet?: RouteTarifFareSet[]; } export interface RouteJourneySegmentTrain extends RouteJourney { @@ -86,17 +80,20 @@ export interface WalkStopInfo { delay?: number; } +export type RoutingStopPlace = MinimalStopPlace & + Pick; + export interface RouteJourneySegmentWalk { type: 'WALK'; - train: ParsedProduct; + train: CommonProductInfo; arrival: WalkStopInfo; departure: WalkStopInfo; /** * @isInt ms */ duration: number; - segmentStart: HafasStation; - segmentDestination: HafasStation; + segmentStart: RoutingStopPlace; + segmentDestination: RoutingStopPlace; } export interface RouteTarifFare { diff --git a/src/types/stationBoard.ts b/src/types/stationBoard.ts deleted file mode 100644 index 7a81d8aff..000000000 --- a/src/types/stationBoard.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { RouteStop } from '@/types/routing'; -import type { - CommonStopInfo, - HafasStation, - ParsedProduct, - RemL, -} from './HAFAS'; - -interface CommonStationBoardEntry { - train: ParsedProduct; - cancelled?: boolean; - finalDestination: string; - jid: string; - stops?: RouteStop[]; - currentStation: HafasStation; - messages?: RemL[]; -} - -export interface ArrivalStationBoardEntry extends CommonStationBoardEntry { - arrival: CommonStopInfo; -} - -export interface DepartureStationBoardEntry extends CommonStationBoardEntry { - departure: CommonStopInfo; -} -export type StationBoardEntry = - | ArrivalStationBoardEntry - | DepartureStationBoardEntry; diff --git a/src/types/stopPlace.ts b/src/types/stopPlace.ts index 7b92256ef..88f88c480 100644 --- a/src/types/stopPlace.ts +++ b/src/types/stopPlace.ts @@ -46,3 +46,39 @@ export interface VRRTrainOccupancy { } export type VRRTrainOccupancyValues = 1 | 2 | 3; + +export interface CommonStopInfo { + /** + * Quelle dieser info ist die Planwagenreihung + */ + isPlan?: boolean; + /** + * Scheduled Platform + */ + scheduledPlatform?: string; + /** + * Best known platform, might be identical to scheduledPlatform + */ + platform?: string; + /** + * scheduled time for this stop + */ + scheduledTime: Date; + /** + * best known time for this stop, might be identical to scheduledTime + */ + time: Date; + /** + * @isInt + */ + delay?: number; + cancelled?: boolean; + additional?: boolean; + noPassengerChange?: boolean; + /** + * Arrival/Departure ID + */ + id?: string; + /** REPORTED Time */ + isRealTime?: boolean; +}