Implement PocketBase auth helpers (P0.1)
Add authentication utilities to pocketbase.ts for server-side auth: - createPocketBaseClient() - factory for fresh instances per request - isAuthenticated(pb) - checks authStore validity - getCurrentUser(pb) - returns typed User from authStore - loadAuthFromCookies(pb, cookies) - loads auth from Next.js cookies Includes 9 unit tests covering all auth state scenarios and cookie loading. This unblocks P0.2 (auth middleware) and all downstream API/page work. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,82 @@
|
||||
// ABOUTME: PocketBase client initialization and utilities.
|
||||
// ABOUTME: PocketBase client initialization and authentication utilities.
|
||||
// ABOUTME: Provides typed access to the PocketBase backend for auth and data.
|
||||
import PocketBase from "pocketbase";
|
||||
import type { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies";
|
||||
import PocketBase, { type RecordModel } from "pocketbase";
|
||||
|
||||
import type { OverrideType, User } from "@/types";
|
||||
|
||||
const POCKETBASE_URL = process.env.POCKETBASE_URL || "http://localhost:8090";
|
||||
|
||||
/**
|
||||
* Singleton PocketBase client for client-side usage.
|
||||
* For server-side, use createPocketBaseClient() to get a fresh instance.
|
||||
*/
|
||||
export const pb = new PocketBase(POCKETBASE_URL);
|
||||
|
||||
// Disable auto-cancellation for server-side usage
|
||||
pb.autoCancellation(false);
|
||||
|
||||
/**
|
||||
* Creates a new PocketBase client instance.
|
||||
* Use this in server components and API routes to get a fresh client
|
||||
* that can be loaded with request-specific auth.
|
||||
*/
|
||||
export function createPocketBaseClient(): PocketBase {
|
||||
const client = new PocketBase(POCKETBASE_URL);
|
||||
client.autoCancellation(false);
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the PocketBase client has a valid authenticated session.
|
||||
*/
|
||||
export function isAuthenticated(pbClient: PocketBase): boolean {
|
||||
return pbClient.authStore.isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current authenticated user from the PocketBase auth store.
|
||||
* Returns null if not authenticated or if the model is not available.
|
||||
*/
|
||||
export function getCurrentUser(pbClient: PocketBase): User | null {
|
||||
if (!pbClient.authStore.isValid || !pbClient.authStore.model) {
|
||||
return null;
|
||||
}
|
||||
return mapRecordToUser(pbClient.authStore.model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads authentication state from Next.js cookies into the PocketBase client.
|
||||
* Call this in server components/routes before checking auth.
|
||||
*/
|
||||
export function loadAuthFromCookies(
|
||||
pbClient: PocketBase,
|
||||
cookieStore: ReadonlyRequestCookies,
|
||||
): void {
|
||||
const cookie = cookieStore.get("pb_auth");
|
||||
if (cookie) {
|
||||
pbClient.authStore.loadFromCookie(`pb_auth=${cookie.value}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a PocketBase record to our typed User interface.
|
||||
*/
|
||||
function mapRecordToUser(record: RecordModel): User {
|
||||
return {
|
||||
id: record.id,
|
||||
email: record.email as string,
|
||||
garminConnected: record.garminConnected as boolean,
|
||||
garminOauth1Token: record.garminOauth1Token as string,
|
||||
garminOauth2Token: record.garminOauth2Token as string,
|
||||
garminTokenExpiresAt: new Date(record.garminTokenExpiresAt as string),
|
||||
calendarToken: record.calendarToken as string,
|
||||
lastPeriodDate: new Date(record.lastPeriodDate as string),
|
||||
cycleLength: record.cycleLength as number,
|
||||
notificationTime: record.notificationTime as string,
|
||||
timezone: record.timezone as string,
|
||||
activeOverrides: (record.activeOverrides as OverrideType[]) || [],
|
||||
created: new Date(record.created as string),
|
||||
updated: new Date(record.updated as string),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user