Add logout functionality and Garmin sync structured logging

- Add POST /api/auth/logout endpoint with tests (5 tests)
- Add logout button to settings page (5 tests)
- Add structured logging to garmin-sync cron (sync start/complete/failure)
- Update IMPLEMENTATION_PLAN.md with spec gap analysis findings
- Total: 835 tests passing across 44 test files

Closes spec gaps from authentication.md (logout) and observability.md (logging)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-11 23:00:54 +00:00
parent e9a77fd79c
commit 13b58c3c32
7 changed files with 411 additions and 7 deletions

View File

@@ -13,6 +13,7 @@ import {
fetchIntensityMinutes,
isTokenExpired,
} from "@/lib/garmin";
import { logger } from "@/lib/logger";
import {
activeUsersGauge,
garminSyncDuration,
@@ -59,6 +60,8 @@ export async function POST(request: Request) {
const today = new Date().toISOString().split("T")[0];
for (const user of users) {
const userSyncStartTime = Date.now();
try {
// Check if tokens are expired
const tokens: GarminTokens = {
@@ -72,6 +75,9 @@ export async function POST(request: Request) {
continue;
}
// Log sync start
logger.info({ userId: user.id }, "Garmin sync start");
// Check for token expiration warnings (exactly 14 or 7 days)
const daysRemaining = daysUntilExpiry(tokens);
if (daysRemaining === 14 || daysRemaining === 7) {
@@ -135,9 +141,26 @@ export async function POST(request: Request) {
notificationSentAt: null,
});
// Log sync complete with metrics
const userSyncDuration = Date.now() - userSyncStartTime;
logger.info(
{
userId: user.id,
duration_ms: userSyncDuration,
metrics: {
bodyBattery: bodyBattery.current,
hrvStatus,
},
},
"Garmin sync complete",
);
result.usersProcessed++;
garminSyncTotal.inc({ status: "success" });
} catch {
} catch (error) {
// Log sync failure
logger.error({ userId: user.id, err: error }, "Garmin sync failure");
result.errors++;
garminSyncTotal.inc({ status: "failure" });
}