feat: rename keep-notes to memento-note, migrate to PostgreSQL, fix MCP bugs

- 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>
This commit is contained in:
Sepehr Ramezani
2026-04-20 20:58:04 +02:00
parent e2ddfa53d4
commit aa6a214f37
3548 changed files with 440 additions and 516800 deletions

View File

@@ -0,0 +1,86 @@
'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" };
}
}