Files
Momento/memento-note/app/actions/title-suggestions.ts
Sepehr Ramezani aa6a214f37 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>
2026-04-20 20:58:04 +02:00

129 lines
3.0 KiB
TypeScript

'use server'
import { auth } from '@/auth'
import { titleSuggestionService } from '@/lib/ai/services/title-suggestion.service'
import { prisma } from '@/lib/prisma'
import { revalidatePath } from 'next/cache'
export interface GenerateTitlesResponse {
suggestions: Array<{
title: string
confidence: number
reasoning?: string
}>
noteId: string
}
/**
* Generate title suggestions for a note
* Triggered when note reaches 50+ words without a title
*/
export async function generateTitleSuggestions(noteId: string): Promise<GenerateTitlesResponse> {
const session = await auth()
if (!session?.user?.id) {
throw new Error('Unauthorized')
}
try {
// Fetch note content
const note = await prisma.note.findUnique({
where: { id: noteId },
select: { id: true, content: true, userId: true }
})
if (!note) {
throw new Error('Note not found')
}
if (note.userId !== session.user.id) {
throw new Error('Forbidden')
}
if (!note.content || note.content.trim().length === 0) {
throw new Error('Note content is empty')
}
// Generate suggestions
const suggestions = await titleSuggestionService.generateSuggestions(note.content)
return {
suggestions,
noteId
}
} catch (error) {
console.error('Error generating title suggestions:', error)
throw error
}
}
/**
* Apply selected title to note
*/
export async function applyTitleSuggestion(
noteId: string,
selectedTitle: string
): Promise<void> {
const session = await auth()
if (!session?.user?.id) {
throw new Error('Unauthorized')
}
try {
// Update note with selected title
await prisma.note.update({
where: {
id: noteId,
userId: session.user.id
},
data: {
title: selectedTitle,
autoGenerated: true,
lastAiAnalysis: new Date()
}
})
revalidatePath('/')
revalidatePath(`/note/${noteId}`)
} catch (error) {
console.error('Error applying title suggestion:', error)
throw error
}
}
/**
* Record user feedback on title suggestions
* (Phase 3 - for improving future suggestions)
*/
export async function recordTitleFeedback(
noteId: string,
selectedTitle: string,
allSuggestions: Array<{ title: string; confidence: number }>
): Promise<void> {
const session = await auth()
if (!session?.user?.id) {
throw new Error('Unauthorized')
}
try {
// Save to AiFeedback table for learning
await prisma.aiFeedback.create({
data: {
noteId,
userId: session.user.id,
feedbackType: 'thumbs_up', // User chose one of our suggestions
feature: 'title_suggestion',
originalContent: JSON.stringify(allSuggestions),
correctedContent: selectedTitle,
metadata: JSON.stringify({
timestamp: new Date().toISOString(),
provider: 'auto' // Will be dynamic based on user settings
})
}
})
} catch (error) {
console.error('Error recording title feedback:', error)
// Don't throw - feedback is optional
}
}