feat: smart note history with manual/auto modes, delete entries, i18n fixes
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m16s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m16s
- Add noteHistoryMode setting (manual default / auto) with DB migration - Manual mode: commit button in editor toolbar creates snapshots on demand - Auto mode: smart snapshots with 20-char diff threshold + 5min cooldown, structural changes (color, pin, archive, labels) bypass cooldown - Add delete individual history entries from history modal - Fix sidebar: Notes nav no longer active on notebook pages - Fix sidebar icon: replace filled Lightbulb with outlined FileText - Fix title suggestions: change from amber to sky blue color scheme - Fix hydration mismatch: add suppressHydrationWarning on locale dates - Complete i18n: add history, sort, and AI chat translations for all 16 languages - Translate French AI assistant section (40+ keys) from English to French - Update README with new features and stack info Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
51
memento-note/app/api/notes/[id]/history/route.ts
Normal file
51
memento-note/app/api/notes/[id]/history/route.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/auth'
|
||||
import { getNoteHistory, restoreNoteVersion } from '@/app/actions/notes'
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const { id } = await params
|
||||
const limitRaw = request.nextUrl.searchParams.get('limit')
|
||||
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : 30
|
||||
const entries = await getNoteHistory(id, Number.isNaN(limit) ? 30 : limit)
|
||||
return NextResponse.json({ success: true, data: entries })
|
||||
} catch (error) {
|
||||
console.error('Error fetching note history:', error)
|
||||
return NextResponse.json({ success: false, error: 'Failed to fetch history' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const { id } = await params
|
||||
const body = await request.json()
|
||||
const historyEntryId = body?.historyEntryId
|
||||
|
||||
if (!historyEntryId || typeof historyEntryId !== 'string') {
|
||||
return NextResponse.json({ success: false, error: 'historyEntryId is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const restoredNote = await restoreNoteVersion(id, historyEntryId)
|
||||
return NextResponse.json({ success: true, data: restoredNote })
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to restore history version'
|
||||
console.error('Error restoring note history:', error)
|
||||
return NextResponse.json({ success: false, error: message }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,13 @@ import { NextRequest, NextResponse } from 'next/server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
import { parseNote } from '@/lib/utils'
|
||||
import {
|
||||
createNoteHistorySnapshot,
|
||||
getNoteHistoryMode,
|
||||
isNoteHistoryEnabledForUser,
|
||||
shouldCaptureHistorySnapshot,
|
||||
shouldCreateAutoSnapshot,
|
||||
} from '@/lib/note-history'
|
||||
|
||||
// GET /api/notes/[id] - Get a single note
|
||||
export async function GET(
|
||||
@@ -134,6 +141,32 @@ export async function PUT(
|
||||
data: updateData,
|
||||
})
|
||||
|
||||
try {
|
||||
const historyEnabled = await isNoteHistoryEnabledForUser(session.user.id)
|
||||
if (historyEnabled && shouldCaptureHistorySnapshot(updateData)) {
|
||||
const mode = await getNoteHistoryMode(session.user.id)
|
||||
if (mode === 'auto') {
|
||||
const shouldAuto = await shouldCreateAutoSnapshot({
|
||||
noteId: id,
|
||||
userId: session.user.id,
|
||||
updateData,
|
||||
existingContent: existingNote.content ?? '',
|
||||
existingTitle: existingNote.title ?? null,
|
||||
})
|
||||
if (shouldAuto) {
|
||||
await createNoteHistorySnapshot({
|
||||
noteId: id,
|
||||
userId: session.user.id,
|
||||
reason: 'api:update',
|
||||
})
|
||||
}
|
||||
}
|
||||
// manual mode: no auto-snapshot
|
||||
}
|
||||
} catch (snapshotError) {
|
||||
console.error('[HISTORY] Failed to create snapshot from /api/notes/[id] PUT:', snapshotError)
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: parseNote(note)
|
||||
|
||||
Reference in New Issue
Block a user