mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 22:52:08 +08:00
feat: Implement delete message functionality and validation in session messages
This commit is contained in:
parent
98ebfd12b3
commit
6bcd941cc6
@ -228,3 +228,53 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
|
||||
res.end()
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteMessage = async (req: Request, res: Response): Promise<Response> => {
|
||||
try {
|
||||
const { agentId, sessionId, messageId: messageIdParam } = req.params
|
||||
const messageId = Number(messageIdParam)
|
||||
|
||||
await verifyAgentAndSession(agentId, sessionId)
|
||||
|
||||
const deleted = await sessionMessageService.deleteSessionMessage(sessionId, messageId)
|
||||
|
||||
if (!deleted) {
|
||||
logger.warn(`Message ${messageId} not found for session ${sessionId}`)
|
||||
return res.status(404).json({
|
||||
error: {
|
||||
message: 'Message not found for this session',
|
||||
type: 'not_found',
|
||||
code: 'session_message_not_found'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
logger.info(`Message ${messageId} deleted successfully for session ${sessionId}`)
|
||||
return res.status(204).send()
|
||||
} catch (error: any) {
|
||||
if (error?.status === 404) {
|
||||
logger.warn('Delete message failed - missing resource', {
|
||||
agentId: req.params.agentId,
|
||||
sessionId: req.params.sessionId,
|
||||
messageId: req.params.messageId,
|
||||
error
|
||||
})
|
||||
return res.status(404).json({
|
||||
error: {
|
||||
message: error.message,
|
||||
type: 'not_found',
|
||||
code: error.code ?? 'session_message_not_found'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
logger.error('Error deleting session message:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to delete session message',
|
||||
type: 'internal_error',
|
||||
code: 'session_message_delete_failed'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
validateSession,
|
||||
validateSessionId,
|
||||
validateSessionMessage,
|
||||
validateSessionMessageId,
|
||||
validateSessionReplace,
|
||||
validateSessionUpdate
|
||||
} from './validators'
|
||||
@ -362,7 +363,7 @@ const agentsRouter = express.Router()
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents:
|
||||
* /agents:
|
||||
* post:
|
||||
* summary: Create a new agent
|
||||
* tags: [Agents]
|
||||
@ -391,7 +392,7 @@ agentsRouter.post('/', validateAgent, handleValidationErrors, agentHandlers.crea
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents:
|
||||
* /agents:
|
||||
* get:
|
||||
* summary: List all agents with pagination
|
||||
* tags: [Agents]
|
||||
@ -429,7 +430,7 @@ agentsRouter.get('/', validatePagination, handleValidationErrors, agentHandlers.
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}:
|
||||
* /agents/{agentId}:
|
||||
* get:
|
||||
* summary: Get agent by ID
|
||||
* tags: [Agents]
|
||||
@ -457,7 +458,7 @@ agentsRouter.get('/', validatePagination, handleValidationErrors, agentHandlers.
|
||||
agentsRouter.get('/:agentId', validateAgentId, handleValidationErrors, agentHandlers.getAgent)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}:
|
||||
* /agents/{agentId}:
|
||||
* put:
|
||||
* summary: Replace agent (full update)
|
||||
* tags: [Agents]
|
||||
@ -497,7 +498,7 @@ agentsRouter.get('/:agentId', validateAgentId, handleValidationErrors, agentHand
|
||||
agentsRouter.put('/:agentId', validateAgentId, validateAgentReplace, handleValidationErrors, agentHandlers.updateAgent)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}:
|
||||
* /agents/{agentId}:
|
||||
* patch:
|
||||
* summary: Update agent (partial update)
|
||||
* tags: [Agents]
|
||||
@ -537,7 +538,7 @@ agentsRouter.put('/:agentId', validateAgentId, validateAgentReplace, handleValid
|
||||
agentsRouter.patch('/:agentId', validateAgentId, validateAgentUpdate, handleValidationErrors, agentHandlers.patchAgent)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}:
|
||||
* /agents/{agentId}:
|
||||
* delete:
|
||||
* summary: Delete agent
|
||||
* tags: [Agents]
|
||||
@ -567,7 +568,7 @@ const createSessionsRouter = (): express.Router => {
|
||||
// Session CRUD routes (nested under agent)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions:
|
||||
* /agents/{agentId}/sessions:
|
||||
* post:
|
||||
* summary: Create a new session for an agent
|
||||
* tags: [Sessions]
|
||||
@ -608,7 +609,7 @@ const createSessionsRouter = (): express.Router => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions:
|
||||
* /agents/{agentId}/sessions:
|
||||
* get:
|
||||
* summary: List sessions for an agent
|
||||
* tags: [Sessions]
|
||||
@ -657,7 +658,7 @@ const createSessionsRouter = (): express.Router => {
|
||||
sessionsRouter.get('/', validatePagination, handleValidationErrors, sessionHandlers.listSessions)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
||||
* /agents/{agentId}/sessions/{sessionId}:
|
||||
* get:
|
||||
* summary: Get session by ID
|
||||
* tags: [Sessions]
|
||||
@ -691,7 +692,7 @@ const createSessionsRouter = (): express.Router => {
|
||||
sessionsRouter.get('/:sessionId', validateSessionId, handleValidationErrors, sessionHandlers.getSession)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
||||
* /agents/{agentId}/sessions/{sessionId}:
|
||||
* put:
|
||||
* summary: Replace session (full update)
|
||||
* tags: [Sessions]
|
||||
@ -743,7 +744,7 @@ const createSessionsRouter = (): express.Router => {
|
||||
)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
||||
* /agents/{agentId}/sessions/{sessionId}:
|
||||
* patch:
|
||||
* summary: Update session (partial update)
|
||||
* tags: [Sessions]
|
||||
@ -795,7 +796,7 @@ const createSessionsRouter = (): express.Router => {
|
||||
)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
||||
* /agents/{agentId}/sessions/{sessionId}:
|
||||
* delete:
|
||||
* summary: Delete session
|
||||
* tags: [Sessions]
|
||||
@ -834,7 +835,7 @@ const createMessagesRouter = (): express.Router => {
|
||||
// Message CRUD routes (nested under agent/session)
|
||||
/**
|
||||
* @swagger
|
||||
* /api/agents/{agentId}/sessions/{sessionId}/messages:
|
||||
* /agents/{agentId}/sessions/{sessionId}/messages:
|
||||
* post:
|
||||
* summary: Create a new message in a session
|
||||
* tags: [Messages]
|
||||
@ -904,6 +905,43 @@ const createMessagesRouter = (): express.Router => {
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
*/
|
||||
messagesRouter.post('/', validateSessionMessage, handleValidationErrors, messageHandlers.createMessage)
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /agents/{agentId}/sessions/{sessionId}/messages/{messageId}:
|
||||
* delete:
|
||||
* summary: Delete a message from a session
|
||||
* tags: [Messages]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: agentId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Agent ID
|
||||
* - in: path
|
||||
* name: sessionId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Session ID
|
||||
* - in: path
|
||||
* name: messageId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Message ID
|
||||
* responses:
|
||||
* 204:
|
||||
* description: Message deleted successfully
|
||||
* 404:
|
||||
* description: Agent, session, or message not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
*/
|
||||
messagesRouter.delete('/:messageId', validateSessionMessageId, handleValidationErrors, messageHandlers.deleteMessage)
|
||||
return messagesRouter
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { CreateSessionMessageRequestSchema } from '@types'
|
||||
import { CreateSessionMessageRequestSchema, SessionMessageIdParamSchema } from '@types'
|
||||
|
||||
import { createZodValidator } from './zodValidator'
|
||||
|
||||
export const validateSessionMessage = createZodValidator({
|
||||
body: CreateSessionMessageRequestSchema
|
||||
})
|
||||
|
||||
export const validateSessionMessageId = createZodValidator({
|
||||
params: SessionMessageIdParamSchema
|
||||
})
|
||||
|
||||
@ -11,7 +11,16 @@ import { handleZoomFactor } from '@main/utils/zoom'
|
||||
import { SpanEntity, TokenUsage } from '@mcp-trace/trace-core'
|
||||
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, UpgradeChannel } from '@shared/config/constant'
|
||||
import { IpcChannel } from '@shared/IpcChannel'
|
||||
import { FileMetadata, Notification, OcrProvider, Provider, Shortcut, SupportedOcrFile, ThemeMode } from '@types'
|
||||
import {
|
||||
AgentPersistedMessage,
|
||||
FileMetadata,
|
||||
Notification,
|
||||
OcrProvider,
|
||||
Provider,
|
||||
Shortcut,
|
||||
SupportedOcrFile,
|
||||
ThemeMode,
|
||||
} from "@types";
|
||||
import checkDiskSpace from 'check-disk-space'
|
||||
import { BrowserWindow, dialog, ipcMain, ProxyConfig, session, shell, systemPreferences, webContents } from 'electron'
|
||||
import fontList from 'font-list'
|
||||
@ -209,14 +218,20 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle(IpcChannel.AgentMessage_GetHistory, async (_event, { sessionId }: { sessionId: string }) => {
|
||||
ipcMain.handle(
|
||||
IpcChannel.AgentMessage_GetHistory,
|
||||
async (
|
||||
_event,
|
||||
{ sessionId }: { sessionId: string }
|
||||
): Promise<AgentPersistedMessage[]> => {
|
||||
try {
|
||||
return await agentMessageRepository.getSessionHistory(sessionId)
|
||||
return await agentMessageRepository.getSessionHistory(sessionId);
|
||||
} catch (error) {
|
||||
logger.error('Failed to get agent session history', error as Error)
|
||||
throw error
|
||||
logger.error("Failed to get agent session history", error as Error);
|
||||
throw error;
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
//only for mac
|
||||
if (isMac) {
|
||||
|
||||
@ -6,7 +6,7 @@ import type {
|
||||
ListOptions
|
||||
} from '@types'
|
||||
import { TextStreamPart } from 'ai'
|
||||
import { desc, eq } from 'drizzle-orm'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
|
||||
import { BaseService } from '../BaseService'
|
||||
import { sessionMessagesTable } from '../database/schema'
|
||||
@ -145,6 +145,16 @@ export class SessionMessageService extends BaseService {
|
||||
return { messages }
|
||||
}
|
||||
|
||||
async deleteSessionMessage(sessionId: string, messageId: number): Promise<boolean> {
|
||||
this.ensureInitialized()
|
||||
|
||||
const result = await this.database
|
||||
.delete(sessionMessagesTable)
|
||||
.where(and(eq(sessionMessagesTable.id, messageId), eq(sessionMessagesTable.session_id, sessionId)))
|
||||
|
||||
return result.rowsAffected > 0
|
||||
}
|
||||
|
||||
async createSessionMessage(
|
||||
session: GetAgentSessionResponse,
|
||||
messageData: CreateSessionMessageRequest,
|
||||
|
||||
@ -206,6 +206,16 @@ export class AgentApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteSessionMessage(agentId: string, sessionId: string, messageId: number): Promise<void> {
|
||||
const base = this.getSessionMessagesPath(agentId, sessionId)
|
||||
const url = `${base}/${messageId}`
|
||||
try {
|
||||
await this.axios.delete(url)
|
||||
} catch (error) {
|
||||
throw processError(error, 'Failed to delete session message.')
|
||||
}
|
||||
}
|
||||
|
||||
public async updateSession(agentId: string, session: UpdateSessionForm): Promise<GetAgentSessionResponse> {
|
||||
const url = this.getSessionPaths(agentId).withId(session.id)
|
||||
try {
|
||||
|
||||
@ -36,7 +36,13 @@ export const DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS: MessageMenubarButtonId[] = [
|
||||
'more-menu'
|
||||
]
|
||||
|
||||
export const SESSION_MESSAGE_MENUBAR_BUTTON_IDS: MessageMenubarButtonId[] = ['copy', 'translate', 'notes', 'more-menu']
|
||||
export const SESSION_MESSAGE_MENUBAR_BUTTON_IDS: MessageMenubarButtonId[] = [
|
||||
'copy',
|
||||
'translate',
|
||||
'notes',
|
||||
'delete',
|
||||
'more-menu'
|
||||
]
|
||||
|
||||
const messageMenubarRegistry = new Map<MessageMenubarScope, MessageMenubarScopeConfig>([
|
||||
[DEFAULT_MESSAGE_MENUBAR_SCOPE, { buttonIds: [...DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS] }],
|
||||
|
||||
@ -267,6 +267,10 @@ export const SessionIdParamSchema = z.object({
|
||||
sessionId: z.string().min(1, 'Session ID is required')
|
||||
})
|
||||
|
||||
export const SessionMessageIdParamSchema = z.object({
|
||||
messageId: z.coerce.number().int().positive('Message ID must be a positive integer')
|
||||
})
|
||||
|
||||
// Query validation schemas
|
||||
export const PaginationQuerySchema = z.object({
|
||||
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user