This commit is contained in:
zramsay 2025-03-11 18:53:09 -04:00
parent 67eb8c3d07
commit 47b9f8264f
4 changed files with 285 additions and 14 deletions

View File

@ -75,9 +75,46 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
);
}
// Use headers for user info if available, otherwise fall back to session
userId = session?.user?.id || headerUser?.id || req.headers.get('x-user-id') || 'unknown';
userEmail = session?.user?.email || headerUser?.email || req.headers.get('x-user-email') || 'unknown@example.com';
// Get user info with lots of fallbacks and detailed logging
const sessionId = session?.user?.id;
const sessionEmail = session?.user?.email;
const headerId = headerUser?.id;
const headerEmail = headerUser?.email;
const headerXId = req.headers.get('x-user-id');
const headerXEmail = req.headers.get('x-user-email');
// Log all possible sources of user data
console.log('All user data sources:', {
sessionId,
sessionEmail,
headerId,
headerEmail,
headerXId,
headerXEmail,
sessionData: session,
headerUserData: headerUser
});
// Use the first valid user ID we can find
userId = sessionId || headerId || headerXId || 'unknown';
// For email, be more strict - never use unknown@example.com unless absolutely necessary
if (sessionEmail && sessionEmail !== 'unknown@example.com') {
userEmail = sessionEmail;
} else if (headerEmail && headerEmail !== 'unknown@example.com') {
userEmail = headerEmail;
} else if (headerXEmail && headerXEmail !== 'unknown@example.com') {
userEmail = headerXEmail;
} else if (userId.includes('@')) {
// If no email but userId looks like email, use that
userEmail = userId;
console.log('Using userId as email since it looks like one:', userId);
} else {
userEmail = 'unknown@example.com';
}
// Log the final decision
console.log('Final user identification:', { userId, userEmail });
}
// Log authentication details

View File

@ -52,14 +52,40 @@ const authOptions = {
callbacks: {
// JWT callback to persist data from the OAuth provider to the JWT
async jwt({ token, user, account, profile, trigger }) {
console.log("JWT Callback:", { tokenSub: token.sub, profile, trigger });
console.log("JWT Callback:", {
tokenSub: token.sub,
tokenEmail: token.email,
userEmail: user?.email,
profile,
trigger,
hasUser: !!user,
hasAccount: !!account
});
// Initial sign-in - add data from the OAuth provider to the token
if (account && profile) {
console.log("Initial sign-in, storing profile data in token");
token.userId = token.sub; // Use sub as the primary userId
token.email = profile.email;
} else if (user) {
// If we have user but no profile, make sure to keep the user data
console.log("User data available, storing in token");
token.userId = token.sub || user.id;
token.email = token.email || user.email;
} else {
// Ensure email is always available for user identification
console.log("No profile or user, using token defaults");
token.userId = token.sub;
// Email might already be in the token from a previous sign-in
}
// Add explicit logging of the token we're returning
console.log("Returning token with data:", {
sub: token.sub,
userId: token.userId,
email: token.email
});
return token;
},
@ -67,15 +93,32 @@ const authOptions = {
async session({ session, token, user }) {
console.log("Session Callback:", {
sessionUserId: session?.user?.id,
sessionEmail: session?.user?.email,
tokenUserId: token?.userId,
tokenSub: token?.sub
tokenSub: token?.sub,
tokenEmail: token?.email,
hasUser: !!user
});
// Ensure user ID is available in the session
// Ensure user ID and email are available in the session
if (session.user) {
// First try to get ID from token.userId, then fall back to token.sub
session.user.id = token.userId || token.sub;
// Make sure we also have email - very important for points system
if (!session.user.email && token.email) {
session.user.email = token.email;
console.log("Added missing email to session from token:", token.email);
}
}
// Detailed logging of the session we're returning
console.log("Returning session with user data:", {
id: session?.user?.id,
email: session?.user?.email,
name: session?.user?.name
});
return session;
}
}

View File

@ -58,15 +58,54 @@ export async function getSessionFromCookie(req: NextRequest) {
};
}
// If we have a session cookie but no headers, we need to persist the session user ID
// If we have a session cookie but no headers, we need to try to extract user data from jwt
if (sessionCookie) {
// For production, we'll just indicate authentication is present
// The JWT session data is handled by NextAuth in the client
console.log('Session cookie authentication is present, relying on client-side session data');
return {
isAuthenticated: true,
sessionPresent: true
};
try {
console.log('Session cookie authentication is present, attempting to extract user data');
// Try to extract the JWT payload - this is a simplified approach to get basic user data
const jwtValue = sessionCookie.value;
if (jwtValue) {
// Decode the JWT - it's base64url encoded
const parts = jwtValue.split('.');
if (parts.length === 3) {
const payloadBase64 = parts[1];
// Convert from base64url to regular string
const jsonStr = Buffer.from(payloadBase64, 'base64').toString();
const payload = JSON.parse(jsonStr);
console.log('Extracted session JWT payload:', {
sub: payload.sub,
email: payload.email,
userId: payload.userId
});
if (payload.sub || payload.userId || payload.email) {
return {
isAuthenticated: true,
sessionPresent: true,
user: {
id: payload.userId || payload.sub,
email: payload.email
}
};
}
}
}
// Fallback if JWT extraction doesn't yield useful info
console.log('Could not extract useful data from cookie, returning sessionPresent only');
return {
isAuthenticated: true,
sessionPresent: true
};
} catch (jwtError) {
console.log('Error parsing JWT from cookie:', jwtError);
return {
isAuthenticated: true,
sessionPresent: true
};
}
}
// Fallback - this shouldn't happen often

152
supabase-schema-updates.sql Normal file
View File

@ -0,0 +1,152 @@
-- Additional SQL functions to improve reliability of user points system
-- Function to get the next transaction ID
CREATE OR REPLACE FUNCTION get_next_transaction_id()
RETURNS BIGINT AS $$
BEGIN
RETURN nextval('point_transactions_id_seq');
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Function to insert a transaction with a specific ID
CREATE OR REPLACE FUNCTION insert_transaction_with_id(
id_num BIGINT,
user_id_text TEXT,
points_num INTEGER,
hash_text TEXT DEFAULT NULL
)
RETURNS BOOLEAN AS $$
BEGIN
INSERT INTO point_transactions (
id,
user_id,
points,
transaction_type,
image_hash,
created_at
)
VALUES (
id_num,
user_id_text::UUID,
points_num,
'image_upload',
hash_text,
now()
);
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Error in insert_transaction_with_id: %', SQLERRM;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Minimal transaction insert function
CREATE OR REPLACE FUNCTION insert_minimal_transaction(
user_id_text TEXT,
points_num INTEGER
)
RETURNS BOOLEAN AS $$
BEGIN
INSERT INTO point_transactions (
user_id,
points,
transaction_type,
created_at
)
VALUES (
user_id_text::UUID,
points_num,
'image_upload',
now()
);
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Error in insert_minimal_transaction: %', SQLERRM;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Function to ensure a user exists, creating if needed
CREATE OR REPLACE FUNCTION ensure_user_exists(
user_id_text TEXT,
email_text TEXT,
initial_points INTEGER DEFAULT 0
)
RETURNS BOOLEAN AS $$
DECLARE
user_exists BOOLEAN;
BEGIN
-- Check if user exists
SELECT EXISTS (
SELECT 1 FROM user_points WHERE user_id = user_id_text::UUID
) INTO user_exists;
-- If user doesn't exist, create them
IF NOT user_exists THEN
INSERT INTO user_points (
user_id,
email,
total_points,
created_at,
updated_at
)
VALUES (
user_id_text::UUID,
email_text,
initial_points,
now(),
now()
);
END IF;
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Error in ensure_user_exists: %', SQLERRM;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Function to force set user points to a specific value
CREATE OR REPLACE FUNCTION force_set_user_points(
user_id_text TEXT,
points_value INTEGER
)
RETURNS BOOLEAN AS $$
BEGIN
UPDATE user_points
SET
total_points = points_value,
updated_at = now()
WHERE user_id = user_id_text::UUID;
RETURN FOUND;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Error in force_set_user_points: %', SQLERRM;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Function to create a user with raw SQL
CREATE OR REPLACE FUNCTION create_user_with_raw_sql(
user_id_text TEXT,
email_text TEXT
)
RETURNS BOOLEAN AS $$
BEGIN
-- Directly execute the SQL insert
EXECUTE format('
INSERT INTO user_points (user_id, email, total_points, created_at, updated_at)
VALUES (%L::UUID, %L, 1, now(), now())
ON CONFLICT (user_id) DO UPDATE
SET updated_at = now()
RETURNING id
', user_id_text, email_text);
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Error in create_user_with_raw_sql: %', SQLERRM;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;