- Rename directory keep-notes -> memento-note with all code references - Prisma: SQLite -> PostgreSQL (both app and MCP server schemas) - Sync MCP schema with main app (add missing fields, relations, indexes) - Delete 17 SQLite migrations (clean slate for PostgreSQL) - Remove SQLite dependencies (@libsql/client, better-sqlite3, etc.) - Fix MCP server: hardcoded Windows DB paths -> DATABASE_URL env var - Fix MCP server: .dockerignore excluded index-sse.js (SSE mode broken) - MCP Dockerfile: node:20 -> node:22 - Docker Compose: add postgres service, remove SQLite volume - Generate favicon.ico, icon-192.png, icon-512.png, apple-icon.png - Update layout.tsx icons and manifest.json for PNG icons - Update all .env files for PostgreSQL - Rewrite README.md with updated sections - Remove mcp-server/node_modules and prisma/client-generated from git tracking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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" };
|
|
}
|
|
}
|