mirror of
http://100.103.83.12:3003/fegger/time_to_leave.git
synced 2026-09-17 09:42:44 +00:00
mobile app phase 1
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import type {
|
||||
GeocodeResult,
|
||||
BikeRoute,
|
||||
CalendarEvent,
|
||||
Journey,
|
||||
Station,
|
||||
} from "@timetoleave/core";
|
||||
|
||||
const DEFAULT_BASE_URL = "";
|
||||
|
||||
function buildUrl(base: string, path: string, params: Record<string, string> = {}): string {
|
||||
const queryString = Object.entries(params)
|
||||
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
||||
.join("&");
|
||||
const full = `${base}${path}`;
|
||||
return queryString ? `${full}?${queryString}` : full;
|
||||
}
|
||||
|
||||
export class ApiClient {
|
||||
private readonly baseUrl: string;
|
||||
|
||||
constructor(baseUrl?: string) {
|
||||
this.baseUrl = baseUrl ?? DEFAULT_BASE_URL;
|
||||
}
|
||||
|
||||
async getHealth(): Promise<{ status: "ok"; uptime: number }> {
|
||||
const res = await fetch(`${this.baseUrl}/api/health`);
|
||||
if (!res.ok) throw new Error(`Health check failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async geocode(name: string, countrycodes?: string): Promise<GeocodeResult[]> {
|
||||
const params: Record<string, string> = { name };
|
||||
if (countrycodes) params.countrycodes = countrycodes;
|
||||
const url = buildUrl(this.baseUrl, "/api/geocode", params);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`Geocode failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async getBikeRoute(
|
||||
fromLat: number,
|
||||
fromLng: number,
|
||||
toLat: number,
|
||||
toLng: number,
|
||||
): Promise<BikeRoute> {
|
||||
const url = buildUrl(this.baseUrl, "/api/bike-route", {
|
||||
fromLat: String(fromLat),
|
||||
fromLng: String(fromLng),
|
||||
toLat: String(toLat),
|
||||
toLng: String(toLng),
|
||||
});
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`Bike route failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async fetchCalendar(url: string, days?: number): Promise<CalendarEvent[]> {
|
||||
const params: Record<string, string> = { url };
|
||||
if (days) params.days = String(days);
|
||||
const api = buildUrl(this.baseUrl, "/api/calendar", params);
|
||||
const res = await fetch(api);
|
||||
if (!res.ok) throw new Error(`Calendar fetch failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async parseCalendarIcs(content: string): Promise<CalendarEvent[]> {
|
||||
const res = await fetch(`${this.baseUrl}/api/calendar/parse`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "text/calendar" },
|
||||
body: content,
|
||||
});
|
||||
if (!res.ok) throw new Error(`Calendar parse failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async searchJourneys(
|
||||
fromStationExtId: string,
|
||||
toStationExtId: string,
|
||||
date: Date,
|
||||
): Promise<Journey[]> {
|
||||
const url = buildUrl(this.baseUrl, "/api/hafas", {
|
||||
from: fromStationExtId,
|
||||
to: toStationExtId,
|
||||
date: date.toISOString(),
|
||||
});
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`Journey search failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async searchStation(query: string): Promise<Station[]> {
|
||||
const url = buildUrl(this.baseUrl, "/api/station", { q: query });
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`Station search failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { ApiClient } from './client';
|
||||
Reference in New Issue
Block a user