## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
'use server'
|
|
|
|
import prisma from '@/lib/prisma'
|
|
import { sendEmail } from '@/lib/mail'
|
|
import bcrypt from 'bcryptjs'
|
|
import { getEmailTemplate } from '@/lib/email-template'
|
|
|
|
// Helper simple pour générer un token sans dépendance externe lourde
|
|
function generateToken() {
|
|
const array = new Uint8Array(32);
|
|
globalThis.crypto.getRandomValues(array);
|
|
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
|
|
export async function forgotPassword(email: string) {
|
|
if (!email) return { error: "Email is required" };
|
|
|
|
try {
|
|
const user = await prisma.user.findUnique({ where: { email: email.toLowerCase() } });
|
|
if (!user) {
|
|
// Pour des raisons de sécurité, on ne dit pas si l'email existe ou pas
|
|
return { success: true };
|
|
}
|
|
|
|
const token = generateToken();
|
|
const expiry = new Date(Date.now() + 3600000); // 1 hour
|
|
|
|
await prisma.user.update({
|
|
where: { id: user.id },
|
|
data: {
|
|
resetToken: token,
|
|
resetTokenExpiry: expiry
|
|
}
|
|
});
|
|
|
|
const resetLink = `${process.env.NEXTAUTH_URL || 'http://localhost:3000'}/reset-password?token=${token}`;
|
|
|
|
const html = getEmailTemplate(
|
|
"Reset your Password",
|
|
"<p>You requested a password reset for your Memento account.</p><p>Click the button below to set a new password. This link is valid for 1 hour.</p>",
|
|
resetLink,
|
|
"Reset Password"
|
|
);
|
|
|
|
await sendEmail({
|
|
to: user.email,
|
|
subject: "Reset your Memento password",
|
|
html
|
|
});
|
|
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error('Forgot password error:', error);
|
|
return { error: "Failed to send reset email" };
|
|
}
|
|
}
|
|
|
|
export async function resetPassword(token: string, newPassword: string) {
|
|
if (!token || !newPassword) return { error: "Missing token or password" };
|
|
|
|
try {
|
|
const user = await prisma.user.findUnique({
|
|
where: { resetToken: token }
|
|
});
|
|
|
|
if (!user || !user.resetTokenExpiry || user.resetTokenExpiry < new Date()) {
|
|
return { error: "Invalid or expired token" };
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
|
|
|
await prisma.user.update({
|
|
where: { id: user.id },
|
|
data: {
|
|
password: hashedPassword,
|
|
resetToken: null,
|
|
resetTokenExpiry: null
|
|
}
|
|
});
|
|
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error('Reset password error:', error);
|
|
return { error: "Failed to reset password" };
|
|
}
|
|
}
|