build errors fixed

This commit is contained in:
dimitar committed 2025-12-18 17:19:11 +01:00
1 parent d3a36b6103
commit 6580564767
8 files changed
+132 -93

No files matched your search

+24 -7
View File
@@ -1,13 +1,21 @@
import { User as SharedUser, Client, FitnessProfile, Attendance, Recommendation, FitnessGoal } from "@fitai/shared";
import {
User as SharedUser,
Client,
FitnessProfile,
Attendance,
Recommendation,
FitnessGoal,
} from "@fitai/shared";
// Database Entity Types
export interface User extends SharedUser {
// Explicitly include gymId so server imports can rely on it even if shared types lag behind build
gymId?: string;
password: string;
}
export type { Client, FitnessProfile, Attendance, Recommendation, FitnessGoal };
// Database Interface - allows us to swap implementations
export interface IDatabase {
// Connection management
@@ -58,7 +66,10 @@ export interface IDatabase {
// Recommendation operations
createRecommendation(
recommendation: Omit<Recommendation, "createdAt" | "approvedAt" | "approvedBy">,
recommendation: Omit<
Recommendation,
"createdAt" | "approvedAt" | "approvedBy"
>,
): Promise<Recommendation>;
getRecommendationsByUserId(userId: string): Promise<Recommendation[]>;
getAllRecommendations(): Promise<Recommendation[]>;
@@ -70,16 +81,22 @@ export interface IDatabase {
// Fitness Goals operations
createFitnessGoal(
goal: Omit<FitnessGoal, "createdAt" | "updatedAt">
goal: Omit<FitnessGoal, "createdAt" | "updatedAt">,
): Promise<FitnessGoal>;
getFitnessGoalById(id: string): Promise<FitnessGoal | null>;
getFitnessGoalsByUserId(userId: string, status?: string): Promise<FitnessGoal[]>;
getFitnessGoalsByUserId(
userId: string,
status?: string,
): Promise<FitnessGoal[]>;
updateFitnessGoal(
id: string,
updates: Partial<FitnessGoal>
updates: Partial<FitnessGoal>,
): Promise<FitnessGoal | null>;
deleteFitnessGoal(id: string): Promise<boolean>;
updateGoalProgress(id: string, currentValue: number): Promise<FitnessGoal | null>;
updateGoalProgress(
id: string,
currentValue: number,
): Promise<FitnessGoal | null>;
completeGoal(id: string): Promise<FitnessGoal | null>;
// Dashboard operations
+60 -50
View File
@@ -1,62 +1,72 @@
import { currentUser } from '@clerk/nextjs/server'
import { IDatabase } from './database/types'
import { currentUser } from "@clerk/nextjs/server";
import type { IDatabase, User } from "./database/types";
export async function ensureUserSynced(userId: string, db: IDatabase) {
const existingUser = await db.getUserById(userId)
if (existingUser) return existingUser
export async function ensureUserSynced(
userId: string,
db: IDatabase,
): Promise<User | null> {
const existingUser = await db.getUserById(userId);
if (existingUser) return existingUser;
console.log('User not found in DB by ID, checking Clerk:', userId)
const clerkUser = await currentUser()
console.log("User not found in DB by ID, checking Clerk:", userId);
const clerkUser = await currentUser();
if (!clerkUser || clerkUser.id !== userId) {
// If we can't get the user from Clerk (e.g. running locally without full auth sync),
// we might want to fail gracefully or throw.
// For now, throw to be safe.
throw new Error('Could not fetch Clerk user details')
}
if (!clerkUser || clerkUser.id !== userId) {
// If we can't get the user from Clerk (e.g. running locally without full auth sync),
// we might want to fail gracefully or throw.
// For now, throw to be safe.
throw new Error("Could not fetch Clerk user details");
}
const email = clerkUser.emailAddresses[0]?.emailAddress
if (!email) throw new Error('User has no email')
const email = clerkUser.emailAddresses[0]?.emailAddress;
if (!email) throw new Error("User has no email");
// Check if user exists by email (e.g. seeded user)
const existingByEmail = await db.getUserByEmail(email)
if (existingByEmail) {
console.log('User found by email but ID mismatch. Migrating ID...', {
oldId: existingByEmail.id,
newId: userId
})
// Check if user exists by email (e.g. seeded user)
const existingByEmail = await db.getUserByEmail(email);
if (existingByEmail) {
console.log("User found by email but ID mismatch. Migrating ID...", {
oldId: existingByEmail.id,
newId: userId,
});
// Update the ID to match Clerk ID
// We need to do this manually via SQL because IDatabase interface might not expose a direct ID update method easily
// But we can use a raw query if we had access, or add a method.
// Since we don't have direct access to `db.db` here (it's hidden behind IDatabase),
// we should add a method to IDatabase or use a workaround.
// Actually, `SQLiteDatabase` is what we have at runtime.
// Let's assume we can cast it or add `updateUserId` to the interface.
// Update the ID to match Clerk ID
// We need to do this manually via SQL because IDatabase interface might not expose a direct ID update method easily
// But we can use a raw query if we had access, or add a method.
// Since we don't have direct access to `db.db` here (it's hidden behind IDatabase),
// we should add a method to IDatabase or use a workaround.
// Actually, `SQLiteDatabase` is what we have at runtime.
// Let's assume we can cast it or add `updateUserId` to the interface.
// For now, let's try to update it using `updateUser` but `updateUser` usually updates fields based on ID.
// We can't update the ID itself using `updateUser(id, { id: newId })` because `updateUser` implementation filters out `id` from updates.
// For now, let's try to update it using `updateUser` but `updateUser` usually updates fields based on ID.
// We can't update the ID itself using `updateUser(id, { id: newId })` because `updateUser` implementation filters out `id` from updates.
// We need to add a method to migrate user ID in IDatabase.
// Or, strictly for this prototype, we can delete the old user and create a new one (DATA LOSS RISK).
// But since it's a seeded super admin with no data, it's fine.
// However, if they had clients, we'd lose the link.
// We need to add a method to migrate user ID in IDatabase.
// Or, strictly for this prototype, we can delete the old user and create a new one (DATA LOSS RISK).
// But since it's a seeded super admin with no data, it's fine.
// However, if they had clients, we'd lose the link.
// Let's add `migrateUserId(oldId, newId)` to IDatabase.
await db.migrateUserId(existingByEmail.id, userId)
return db.getUserById(userId)
}
// Let's add `migrateUserId(oldId, newId)` to IDatabase.
await db.migrateUserId(existingByEmail.id, userId);
return db.getUserById(userId);
}
console.log('Creating new user from Clerk data:', userId)
const user = await db.createUser({
id: userId,
email,
firstName: clerkUser.firstName || '',
lastName: clerkUser.lastName || '',
password: '', // Managed by Clerk
phone: clerkUser.phoneNumbers[0]?.phoneNumber || undefined,
role: (clerkUser.publicMetadata.role as 'admin' | 'client' | 'superAdmin') || 'client'
})
console.log("Creating new user from Clerk data:", userId);
const user = await db.createUser({
id: userId,
email,
firstName: clerkUser.firstName || "",
lastName: clerkUser.lastName || "",
password: "", // Managed by Clerk
phone: clerkUser.phoneNumbers[0]?.phoneNumber || undefined,
gymId: (clerkUser.publicMetadata.gymId as string | undefined) || undefined,
role: ((): any => {
const r = clerkUser.publicMetadata.role as string | undefined;
return r &&
["superAdmin", "admin", "trainer", "client", "generalUser"].includes(r)
? r
: "client";
})(),
});
return user
return user;
}