This commit is contained in:
Dominik Klarkowski
2026-06-18 11:02:31 +02:00
parent d00a5a42ac
commit 047e580da0
32 changed files with 735 additions and 189 deletions

View File

@@ -4,6 +4,7 @@ import { revalidatePath } from "next/cache";
import type { GarminConnect } from "garmin-connect";
import {
GarminLoginRequiredError,
GarminCredentialsMissingError,
beginGarminLogin,
completeGarminMfaLogin,
fetchActivityRoutePoints,
@@ -23,19 +24,20 @@ import {
saveOauth1Token,
savePendingMfaState,
} from "@/lib/models/garmin-auth";
import { getCurrentUserId } from "@/lib/session";
export type SyncGarminState = { error: string } | { success: string } | { mfaRequired: true } | null;
async function syncWithClient(client: GarminConnect): Promise<SyncGarminState> {
const since = await getLastSyncAt();
async function syncWithClient(userId: string, client: GarminConnect): Promise<SyncGarminState> {
const since = await getLastSyncAt(userId);
const activities = await fetchRunningActivities(client);
const newCount = activities.filter((activity) => !since || activity.startTime > since).length;
for (const activity of activities) {
await upsertRunningActivity(activity);
await upsertRunningActivity(userId, activity);
}
await setLastSyncAt(new Date());
await setLastSyncAt(userId, new Date());
revalidatePath("/running");
revalidatePath("/settings");
@@ -45,39 +47,45 @@ async function syncWithClient(client: GarminConnect): Promise<SyncGarminState> {
}
export async function syncGarminActivities(): Promise<SyncGarminState> {
const userId = await getCurrentUserId();
try {
const client = await getAuthorizedClient();
return await syncWithClient(client);
const client = await getAuthorizedClient(userId);
return await syncWithClient(userId, client);
} catch (error) {
if (error instanceof GarminCredentialsMissingError) {
return { error: error.message };
}
if (!(error instanceof GarminLoginRequiredError)) {
return { error: error instanceof Error ? error.message : "Synchronizacja z Garmin nie powiodła się." };
}
}
try {
const result = await beginGarminLogin();
const result = await beginGarminLogin(userId);
if ("mfaRequired" in result) {
await savePendingMfaState(result.pendingState);
await savePendingMfaState(userId, result.pendingState);
return { mfaRequired: true };
}
await saveOauth1Token(result.oauth1Token);
return await syncWithClient(result.client);
await saveOauth1Token(userId, result.oauth1Token);
return await syncWithClient(userId, result.client);
} catch (error) {
return { error: error instanceof Error ? error.message : "Logowanie do Garmin nie powiodło się." };
}
}
export async function submitGarminMfaCode(code: string): Promise<SyncGarminState> {
const pending = await getPendingMfaState();
const userId = await getCurrentUserId();
const pending = await getPendingMfaState(userId);
if (!pending) {
return { error: "Sesja logowania do Garmin wygasła. Kliknij \"Synchronizuj z Garmin\" ponownie." };
}
try {
const result = await completeGarminMfaLogin(pending, code);
await saveOauth1Token(result.oauth1Token);
await clearPendingMfaState();
return await syncWithClient(result.client);
await saveOauth1Token(userId, result.oauth1Token);
await clearPendingMfaState(userId);
return await syncWithClient(userId, result.client);
} catch (error) {
return { error: error instanceof Error ? error.message : "Weryfikacja kodu MFA nie powiodła się." };
}
@@ -86,12 +94,13 @@ export async function submitGarminMfaCode(code: string): Promise<SyncGarminState
export type LoadRouteState = { error: string } | { success: true } | null;
export async function loadActivityRoute(activityMongoId: string): Promise<LoadRouteState> {
const activity = await getRunningActivity(activityMongoId);
const userId = await getCurrentUserId();
const activity = await getRunningActivity(userId, activityMongoId);
if (!activity) return { error: "Nie znaleziono aktywności." };
let client: GarminConnect;
try {
client = await getAuthorizedClient();
client = await getAuthorizedClient(userId);
} catch {
return { error: "Brak połączenia z Garmin Connect. Wykonaj synchronizację." };
}
@@ -99,7 +108,7 @@ export async function loadActivityRoute(activityMongoId: string): Promise<LoadRo
try {
const result = await fetchActivityRoutePoints(client, activity.garminActivityId);
if (!result) return { error: "Brak danych GPS dla tej aktywności." };
await setRunningActivityRoutePoints(activity.garminActivityId, result.points, result.elevationProfile);
await setRunningActivityRoutePoints(userId, activity.garminActivityId, result.points, result.elevationProfile);
revalidatePath(`/running/${activityMongoId}`);
return { success: true };
} catch (error) {