chore: clean up repo for public release
- Remove BMAD framework, IDE configs, dev screenshots, test files, internal docs, and backup files - Rename keep-notes/ to memento-note/ - Update all references from keep-notes to memento-note - Add Apache 2.0 license with Commons Clause (non-commercial restriction) - Add clean .gitignore and .env.docker.example
This commit is contained in:
96
memento-note/app/api/notes/[id]/move/route.ts
Normal file
96
memento-note/app/api/notes/[id]/move/route.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
import { reconcileLabelsAfterNoteMove } from '@/app/actions/notes'
|
||||
|
||||
// POST /api/notes/[id]/move - Move a note to a notebook (or to Inbox)
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const { id } = await params
|
||||
const body = await request.json()
|
||||
const { notebookId } = body
|
||||
|
||||
// Get the note
|
||||
const note = await prisma.note.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
notebookId: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!note) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note not found' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
// Verify ownership
|
||||
if (note.userId !== session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Forbidden' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// If notebookId is provided, verify it exists and belongs to the user
|
||||
if (notebookId !== null && notebookId !== '') {
|
||||
const notebook = await prisma.notebook.findUnique({
|
||||
where: { id: notebookId },
|
||||
select: { userId: true }
|
||||
})
|
||||
|
||||
if (!notebook || notebook.userId !== session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Notebook not found or unauthorized' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the note's notebook
|
||||
// notebookId = null or "" means move to Inbox (Notes générales)
|
||||
const targetNotebookId = notebookId && notebookId !== '' ? notebookId : null
|
||||
|
||||
const updatedNote = await prisma.note.update({
|
||||
where: { id },
|
||||
data: {
|
||||
notebookId: targetNotebookId
|
||||
},
|
||||
include: {
|
||||
notebook: {
|
||||
select: { id: true, name: true }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await reconcileLabelsAfterNoteMove(id, targetNotebookId)
|
||||
|
||||
// No revalidatePath('/') here — the client-side triggerRefresh() in
|
||||
// notebooks-context.tsx handles the refresh. Avoiding server-side
|
||||
// revalidation prevents a double-refresh (server + client).
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: updatedNote,
|
||||
message: notebookId && notebookId !== ''
|
||||
? `Note moved to "${updatedNote.notebook?.name || 'notebook'}"`
|
||||
: 'Note moved to Inbox'
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to move note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
193
memento-note/app/api/notes/[id]/route.ts
Normal file
193
memento-note/app/api/notes/[id]/route.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
import { parseNote } from '@/lib/utils'
|
||||
|
||||
// GET /api/notes/[id] - Get a single note
|
||||
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 note = await prisma.note.findUnique({
|
||||
where: { id }
|
||||
})
|
||||
|
||||
if (!note) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note not found' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (note.userId !== session.user.id) {
|
||||
const share = await prisma.noteShare.findUnique({
|
||||
where: {
|
||||
noteId_userId: {
|
||||
noteId: note.id,
|
||||
userId: session.user.id
|
||||
}
|
||||
}
|
||||
})
|
||||
if (!share || share.status !== 'accepted') {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Forbidden' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: parseNote(note)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching note:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/notes/[id] - Update a note
|
||||
export async function PUT(
|
||||
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 existingNote = await prisma.note.findUnique({
|
||||
where: { id }
|
||||
})
|
||||
|
||||
if (!existingNote) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note not found' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (existingNote.userId !== session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Forbidden' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const updateData: any = { ...body }
|
||||
|
||||
if ('checkItems' in body) {
|
||||
updateData.checkItems = body.checkItems ?? null
|
||||
}
|
||||
if ('labels' in body) {
|
||||
updateData.labels = body.labels ?? null
|
||||
}
|
||||
|
||||
// Only update if data actually changed
|
||||
const hasChanges = Object.keys(updateData).some((key) => {
|
||||
const newValue = updateData[key]
|
||||
const oldValue = (existingNote as any)[key]
|
||||
// Handle arrays/objects by comparing JSON
|
||||
if (typeof newValue === 'object' && newValue !== null) {
|
||||
return JSON.stringify(newValue) !== JSON.stringify(oldValue)
|
||||
}
|
||||
return newValue !== oldValue
|
||||
})
|
||||
|
||||
// If no changes, return existing note without updating timestamp
|
||||
if (!hasChanges) {
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: parseNote(existingNote),
|
||||
})
|
||||
}
|
||||
|
||||
const note = await prisma.note.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: parseNote(note)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error updating note:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to update note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/notes/[id] - Delete a note
|
||||
export async function DELETE(
|
||||
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 existingNote = await prisma.note.findUnique({
|
||||
where: { id }
|
||||
})
|
||||
|
||||
if (!existingNote) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note not found' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (existingNote.userId !== session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Forbidden' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
await prisma.note.update({
|
||||
where: { id },
|
||||
data: { trashedAt: new Date() }
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Note moved to trash'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error deleting note:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to delete note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
70
memento-note/app/api/notes/delete-all/route.ts
Normal file
70
memento-note/app/api/notes/delete-all/route.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/auth'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import { deleteImageFileSafely, parseImageUrls } from '@/lib/image-cleanup'
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
// Check authentication
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch notes with images before deleting for cleanup
|
||||
const notesWithImages = await prisma.note.findMany({
|
||||
where: { userId: session.user.id },
|
||||
select: { id: true, images: true },
|
||||
})
|
||||
|
||||
// Delete all notes for the user (cascade will handle labels-note relationships)
|
||||
const result = await prisma.note.deleteMany({
|
||||
where: {
|
||||
userId: session.user.id
|
||||
}
|
||||
})
|
||||
|
||||
// Clean up image files from disk (best-effort, don't block response)
|
||||
const imageCleanup = Promise.allSettled(
|
||||
notesWithImages.flatMap(note =>
|
||||
parseImageUrls(note.images).map(url => deleteImageFileSafely(url, note.id))
|
||||
)
|
||||
)
|
||||
|
||||
// Delete all labels for the user
|
||||
await prisma.label.deleteMany({
|
||||
where: {
|
||||
userId: session.user.id
|
||||
}
|
||||
})
|
||||
|
||||
// Delete all notebooks for the user
|
||||
await prisma.notebook.deleteMany({
|
||||
where: {
|
||||
userId: session.user.id
|
||||
}
|
||||
})
|
||||
|
||||
// Revalidate paths
|
||||
revalidatePath('/')
|
||||
revalidatePath('/settings/data')
|
||||
|
||||
// Await cleanup in background (don't block response)
|
||||
imageCleanup.catch(() => {})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
deletedNotes: result.count
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Delete all error:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to delete notes' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
121
memento-note/app/api/notes/export/route.ts
Normal file
121
memento-note/app/api/notes/export/route.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/auth'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
// Check authentication
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch all notes with related data
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
trashedAt: null
|
||||
},
|
||||
include: {
|
||||
labelRelations: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true
|
||||
}
|
||||
},
|
||||
notebook: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
})
|
||||
|
||||
// Fetch labels separately
|
||||
const labels = await prisma.label.findMany({
|
||||
where: {
|
||||
userId: session.user.id
|
||||
},
|
||||
include: {
|
||||
notes: {
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Fetch notebooks
|
||||
const notebooks = await prisma.notebook.findMany({
|
||||
where: {
|
||||
userId: session.user.id
|
||||
},
|
||||
include: {
|
||||
notes: {
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Create export object
|
||||
const exportData = {
|
||||
version: '1.0.0',
|
||||
exportDate: new Date().toISOString(),
|
||||
user: {
|
||||
id: session.user.id,
|
||||
email: session.user.email,
|
||||
name: session.user.name
|
||||
},
|
||||
data: {
|
||||
labels: labels.map(label => ({
|
||||
id: label.id,
|
||||
name: label.name,
|
||||
color: label.color,
|
||||
noteCount: label.notes.length
|
||||
})),
|
||||
notebooks: notebooks.map(notebook => ({
|
||||
id: notebook.id,
|
||||
name: notebook.name,
|
||||
noteCount: notebook.notes.length
|
||||
})),
|
||||
notes: notes.map(note => ({
|
||||
id: note.id,
|
||||
title: note.title,
|
||||
content: note.content,
|
||||
createdAt: note.createdAt,
|
||||
updatedAt: note.updatedAt,
|
||||
isPinned: note.isPinned,
|
||||
notebookId: note.notebookId,
|
||||
labelRelations: note.labelRelations.map(label => ({
|
||||
id: label.id,
|
||||
name: label.name
|
||||
}))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// Return as JSON file
|
||||
const jsonString = JSON.stringify(exportData, null, 2)
|
||||
return new NextResponse(jsonString, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Disposition': `attachment; filename="memento-export-${new Date().toISOString().split('T')[0]}.json"`
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Export error:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to export notes' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
157
memento-note/app/api/notes/import/route.ts
Normal file
157
memento-note/app/api/notes/import/route.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/auth'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
// Check authentication
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Parse form data
|
||||
const formData = await req.formData()
|
||||
const file = formData.get('file') as File
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'No file provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Parse JSON file
|
||||
const text = await file.text()
|
||||
let importData: any
|
||||
|
||||
try {
|
||||
importData = JSON.parse(text)
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Invalid JSON file' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Validate import data structure
|
||||
if (!importData.data || !importData.data.notes) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Invalid import format' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
let importedNotes = 0
|
||||
let importedLabels = 0
|
||||
let importedNotebooks = 0
|
||||
|
||||
// Import labels first
|
||||
if (importData.data.labels && Array.isArray(importData.data.labels)) {
|
||||
for (const label of importData.data.labels) {
|
||||
// Check if label already exists
|
||||
const existing = await prisma.label.findFirst({
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
name: label.name
|
||||
}
|
||||
})
|
||||
|
||||
if (!existing) {
|
||||
await prisma.label.create({
|
||||
data: {
|
||||
userId: session.user.id,
|
||||
name: label.name,
|
||||
color: label.color
|
||||
}
|
||||
})
|
||||
importedLabels++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import notebooks
|
||||
const notebookIdMap = new Map<string, string>()
|
||||
if (importData.data.notebooks && Array.isArray(importData.data.notebooks)) {
|
||||
for (const notebook of importData.data.notebooks) {
|
||||
// Check if notebook already exists
|
||||
const existing = await prisma.notebook.findFirst({
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
name: notebook.name
|
||||
}
|
||||
})
|
||||
|
||||
let newNotebookId
|
||||
if (!existing) {
|
||||
const created = await prisma.notebook.create({
|
||||
data: {
|
||||
userId: session.user.id,
|
||||
name: notebook.name,
|
||||
order: 0
|
||||
}
|
||||
})
|
||||
newNotebookId = created.id
|
||||
notebookIdMap.set(notebook.id, newNotebookId)
|
||||
importedNotebooks++
|
||||
} else {
|
||||
notebookIdMap.set(notebook.id, existing.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import notes
|
||||
if (importData.data.notes && Array.isArray(importData.data.notes)) {
|
||||
for (const note of importData.data.notes) {
|
||||
// Map notebook ID
|
||||
const mappedNotebookId = notebookIdMap.get(note.notebookId) || null
|
||||
|
||||
// Get label IDs
|
||||
const labels = await prisma.label.findMany({
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
name: {
|
||||
in: note.labels.map((l: any) => l.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Create note
|
||||
await prisma.note.create({
|
||||
data: {
|
||||
userId: session.user.id,
|
||||
title: note.title || 'Untitled',
|
||||
content: note.content,
|
||||
isPinned: note.isPinned || false,
|
||||
notebookId: mappedNotebookId,
|
||||
labelRelations: {
|
||||
connect: labels.map(label => ({ id: label.id }))
|
||||
}
|
||||
}
|
||||
})
|
||||
importedNotes++
|
||||
}
|
||||
}
|
||||
|
||||
// Revalidate paths
|
||||
revalidatePath('/')
|
||||
revalidatePath('/settings/data')
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
count: importedNotes,
|
||||
labels: importedLabels,
|
||||
notebooks: importedNotebooks
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Import error:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to import notes' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
230
memento-note/app/api/notes/route.ts
Normal file
230
memento-note/app/api/notes/route.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
import { parseNote } from '@/lib/utils'
|
||||
|
||||
// GET /api/notes - Get all notes
|
||||
export async function GET(request: NextRequest) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const includeArchived = searchParams.get('archived') === 'true'
|
||||
const search = searchParams.get('search')
|
||||
|
||||
let where: any = {
|
||||
userId: session.user.id,
|
||||
trashedAt: null
|
||||
}
|
||||
|
||||
if (!includeArchived) {
|
||||
where.isArchived = false
|
||||
}
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ title: { contains: search, mode: 'insensitive' } },
|
||||
{ content: { contains: search, mode: 'insensitive' } }
|
||||
]
|
||||
}
|
||||
|
||||
const notes = await prisma.note.findMany({
|
||||
where,
|
||||
orderBy: [
|
||||
{ isPinned: 'desc' },
|
||||
{ order: 'asc' },
|
||||
{ updatedAt: 'desc' }
|
||||
]
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: notes.map(parseNote)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching notes:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch notes' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/notes - Create a new note
|
||||
export async function POST(request: NextRequest) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { title, content, color, type, checkItems, labels, images } = body
|
||||
|
||||
if (!content && type !== 'checklist') {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Content is required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const note = await prisma.note.create({
|
||||
data: {
|
||||
userId: session.user.id,
|
||||
title: title || null,
|
||||
content: content || '',
|
||||
color: color || 'default',
|
||||
type: type || 'text',
|
||||
checkItems: checkItems ?? null,
|
||||
labels: labels ?? null,
|
||||
images: images ?? null,
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: parseNote(note)
|
||||
}, { status: 201 })
|
||||
} catch (error) {
|
||||
console.error('Error creating note:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to create note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/notes - Update an existing note
|
||||
export async function PUT(request: NextRequest) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { id, title, content, color, type, checkItems, labels, isPinned, isArchived, images } = body
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note ID is required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const existingNote = await prisma.note.findUnique({
|
||||
where: { id }
|
||||
})
|
||||
|
||||
if (!existingNote) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note not found' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (existingNote.userId !== session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Forbidden' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
const updateData: any = {}
|
||||
|
||||
if (title !== undefined) updateData.title = title
|
||||
if (content !== undefined) updateData.content = content
|
||||
if (color !== undefined) updateData.color = color
|
||||
if (type !== undefined) updateData.type = type
|
||||
if (checkItems !== undefined) updateData.checkItems = checkItems ?? null
|
||||
if (labels !== undefined) updateData.labels = labels ?? null
|
||||
if (isPinned !== undefined) updateData.isPinned = isPinned
|
||||
if (isArchived !== undefined) updateData.isArchived = isArchived
|
||||
if (images !== undefined) updateData.images = images ?? null
|
||||
|
||||
const note = await prisma.note.update({
|
||||
where: { id },
|
||||
data: updateData
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: parseNote(note)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error updating note:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to update note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/notes?id=xxx - Delete a note
|
||||
export async function DELETE(request: NextRequest) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Unauthorized' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const id = searchParams.get('id')
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note ID is required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const existingNote = await prisma.note.findUnique({
|
||||
where: { id }
|
||||
})
|
||||
|
||||
if (!existingNote) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Note not found' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (existingNote.userId !== session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Forbidden' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
await prisma.note.update({
|
||||
where: { id },
|
||||
data: { trashedAt: new Date() }
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Note moved to trash'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error deleting note:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to delete note' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user