mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
Implement anonymous stats migration on signin
- Fix AuthModal to pass anonymousId on both signin and signup - Add comprehensive migration logic in signin that moves anonymous completion stats to authenticated user - Implement deduplication algorithm to handle overlapping completion dates - Maintain earliest completion when duplicates exist 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -95,7 +95,7 @@
|
|||||||
method="POST"
|
method="POST"
|
||||||
action={mode === 'signin' ? '/auth/signin' : '/auth/signup'}
|
action={mode === 'signin' ? '/auth/signin' : '/auth/signup'}
|
||||||
use:enhance={({ formData }) => {
|
use:enhance={({ formData }) => {
|
||||||
if (mode === 'signup' && anonymousId) {
|
if (anonymousId) {
|
||||||
formData.append('anonymousId', anonymousId);
|
formData.append('anonymousId', anonymousId);
|
||||||
}
|
}
|
||||||
handleSubmit();
|
handleSubmit();
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { redirect, fail } from '@sveltejs/kit';
|
import { redirect, fail } from '@sveltejs/kit';
|
||||||
import type { Actions } from './$types';
|
import type { Actions } from './$types';
|
||||||
import * as auth from '$lib/server/auth';
|
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 = {
|
export const actions: Actions = {
|
||||||
default: async ({ request, cookies }) => {
|
default: async ({ request, cookies }) => {
|
||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const email = data.get('email')?.toString();
|
const email = data.get('email')?.toString();
|
||||||
const password = data.get('password')?.toString();
|
const password = data.get('password')?.toString();
|
||||||
|
const anonymousId = data.get('anonymousId')?.toString();
|
||||||
|
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
return fail(400, { error: 'Email and password are required' });
|
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' });
|
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<string, typeof allUserCompletions>();
|
||||||
|
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
|
// Create session
|
||||||
const sessionToken = auth.generateSessionToken();
|
const sessionToken = auth.generateSessionToken();
|
||||||
const session = await auth.createSession(sessionToken, user.id);
|
const session = await auth.createSession(sessionToken, user.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user