diff --git a/src/lib/components/AuthModal.svelte b/src/lib/components/AuthModal.svelte index 7cab036..3f184f2 100644 --- a/src/lib/components/AuthModal.svelte +++ b/src/lib/components/AuthModal.svelte @@ -95,7 +95,7 @@ method="POST" action={mode === 'signin' ? '/auth/signin' : '/auth/signup'} use:enhance={({ formData }) => { - if (mode === 'signup' && anonymousId) { + if (anonymousId) { formData.append('anonymousId', anonymousId); } handleSubmit(); diff --git a/src/routes/auth/signin/+page.server.ts b/src/routes/auth/signin/+page.server.ts index 95e7fa9..394f096 100644 --- a/src/routes/auth/signin/+page.server.ts +++ b/src/routes/auth/signin/+page.server.ts @@ -1,12 +1,16 @@ import { redirect, fail } from '@sveltejs/kit'; import type { Actions } from './$types'; import * as auth from '$lib/server/auth'; +import { db } from '$lib/server/db'; +import { dailyCompletions } from '$lib/server/db/schema'; +import { eq, inArray } from 'drizzle-orm'; export const actions: Actions = { default: async ({ request, cookies }) => { const data = await request.formData(); const email = data.get('email')?.toString(); const password = data.get('password')?.toString(); + const anonymousId = data.get('anonymousId')?.toString(); if (!email || !password) { return fail(400, { error: 'Email and password are required' }); @@ -35,6 +39,63 @@ export const actions: Actions = { return fail(400, { error: 'Invalid email or password' }); } + // Migrate anonymous stats if different anonymous ID + if (anonymousId && anonymousId !== user.id) { + try { + // Update all daily completions from the local anonymous ID to the user's ID + await db + .update(dailyCompletions) + .set({ anonymousId: user.id }) + .where(eq(dailyCompletions.anonymousId, anonymousId)); + + console.log(`Migrated stats from ${anonymousId} to ${user.id}`); + + // Deduplicate any entries for the same date after migration + const allUserCompletions = await db + .select() + .from(dailyCompletions) + .where(eq(dailyCompletions.anonymousId, user.id)); + + // Group by date to find duplicates + const dateGroups = new Map(); + for (const completion of allUserCompletions) { + const date = completion.date; + if (!dateGroups.has(date)) { + dateGroups.set(date, []); + } + dateGroups.get(date)!.push(completion); + } + + // Process dates with duplicates + const duplicateIds: string[] = []; + for (const [date, completions] of dateGroups) { + if (completions.length > 1) { + // Sort by completedAt timestamp (earliest first) + completions.sort((a, b) => a.completedAt.getTime() - b.completedAt.getTime()); + + // Keep the first (earliest), mark the rest for deletion + const toDelete = completions.slice(1); + duplicateIds.push(...toDelete.map(c => c.id)); + + console.log(`Found ${completions.length} duplicates for date ${date}, keeping earliest, deleting ${toDelete.length}`); + } + } + + // Delete duplicate entries + if (duplicateIds.length > 0) { + await db + .delete(dailyCompletions) + .where(inArray(dailyCompletions.id, duplicateIds)); + + console.log(`Deleted ${duplicateIds.length} duplicate completion entries`); + } + + } catch (error) { + console.error('Error migrating anonymous stats:', error); + // Don't fail the signin if stats migration fails + } + } + // Create session const sessionToken = auth.generateSessionToken(); const session = await auth.createSession(sessionToken, user.id);