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()
|
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,
|
validateSession,
|
||||||
validateSessionId,
|
validateSessionId,
|
||||||
validateSessionMessage,
|
validateSessionMessage,
|
||||||
|
validateSessionMessageId,
|
||||||
validateSessionReplace,
|
validateSessionReplace,
|
||||||
validateSessionUpdate
|
validateSessionUpdate
|
||||||
} from './validators'
|
} from './validators'
|
||||||
@ -362,7 +363,7 @@ const agentsRouter = express.Router()
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents:
|
* /agents:
|
||||||
* post:
|
* post:
|
||||||
* summary: Create a new agent
|
* summary: Create a new agent
|
||||||
* tags: [Agents]
|
* tags: [Agents]
|
||||||
@ -391,7 +392,7 @@ agentsRouter.post('/', validateAgent, handleValidationErrors, agentHandlers.crea
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents:
|
* /agents:
|
||||||
* get:
|
* get:
|
||||||
* summary: List all agents with pagination
|
* summary: List all agents with pagination
|
||||||
* tags: [Agents]
|
* tags: [Agents]
|
||||||
@ -429,7 +430,7 @@ agentsRouter.get('/', validatePagination, handleValidationErrors, agentHandlers.
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}:
|
* /agents/{agentId}:
|
||||||
* get:
|
* get:
|
||||||
* summary: Get agent by ID
|
* summary: Get agent by ID
|
||||||
* tags: [Agents]
|
* tags: [Agents]
|
||||||
@ -457,7 +458,7 @@ agentsRouter.get('/', validatePagination, handleValidationErrors, agentHandlers.
|
|||||||
agentsRouter.get('/:agentId', validateAgentId, handleValidationErrors, agentHandlers.getAgent)
|
agentsRouter.get('/:agentId', validateAgentId, handleValidationErrors, agentHandlers.getAgent)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}:
|
* /agents/{agentId}:
|
||||||
* put:
|
* put:
|
||||||
* summary: Replace agent (full update)
|
* summary: Replace agent (full update)
|
||||||
* tags: [Agents]
|
* tags: [Agents]
|
||||||
@ -497,7 +498,7 @@ agentsRouter.get('/:agentId', validateAgentId, handleValidationErrors, agentHand
|
|||||||
agentsRouter.put('/:agentId', validateAgentId, validateAgentReplace, handleValidationErrors, agentHandlers.updateAgent)
|
agentsRouter.put('/:agentId', validateAgentId, validateAgentReplace, handleValidationErrors, agentHandlers.updateAgent)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}:
|
* /agents/{agentId}:
|
||||||
* patch:
|
* patch:
|
||||||
* summary: Update agent (partial update)
|
* summary: Update agent (partial update)
|
||||||
* tags: [Agents]
|
* tags: [Agents]
|
||||||
@ -537,7 +538,7 @@ agentsRouter.put('/:agentId', validateAgentId, validateAgentReplace, handleValid
|
|||||||
agentsRouter.patch('/:agentId', validateAgentId, validateAgentUpdate, handleValidationErrors, agentHandlers.patchAgent)
|
agentsRouter.patch('/:agentId', validateAgentId, validateAgentUpdate, handleValidationErrors, agentHandlers.patchAgent)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}:
|
* /agents/{agentId}:
|
||||||
* delete:
|
* delete:
|
||||||
* summary: Delete agent
|
* summary: Delete agent
|
||||||
* tags: [Agents]
|
* tags: [Agents]
|
||||||
@ -567,7 +568,7 @@ const createSessionsRouter = (): express.Router => {
|
|||||||
// Session CRUD routes (nested under agent)
|
// Session CRUD routes (nested under agent)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions:
|
* /agents/{agentId}/sessions:
|
||||||
* post:
|
* post:
|
||||||
* summary: Create a new session for an agent
|
* summary: Create a new session for an agent
|
||||||
* tags: [Sessions]
|
* tags: [Sessions]
|
||||||
@ -608,7 +609,7 @@ const createSessionsRouter = (): express.Router => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions:
|
* /agents/{agentId}/sessions:
|
||||||
* get:
|
* get:
|
||||||
* summary: List sessions for an agent
|
* summary: List sessions for an agent
|
||||||
* tags: [Sessions]
|
* tags: [Sessions]
|
||||||
@ -657,7 +658,7 @@ const createSessionsRouter = (): express.Router => {
|
|||||||
sessionsRouter.get('/', validatePagination, handleValidationErrors, sessionHandlers.listSessions)
|
sessionsRouter.get('/', validatePagination, handleValidationErrors, sessionHandlers.listSessions)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
* /agents/{agentId}/sessions/{sessionId}:
|
||||||
* get:
|
* get:
|
||||||
* summary: Get session by ID
|
* summary: Get session by ID
|
||||||
* tags: [Sessions]
|
* tags: [Sessions]
|
||||||
@ -691,7 +692,7 @@ const createSessionsRouter = (): express.Router => {
|
|||||||
sessionsRouter.get('/:sessionId', validateSessionId, handleValidationErrors, sessionHandlers.getSession)
|
sessionsRouter.get('/:sessionId', validateSessionId, handleValidationErrors, sessionHandlers.getSession)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
* /agents/{agentId}/sessions/{sessionId}:
|
||||||
* put:
|
* put:
|
||||||
* summary: Replace session (full update)
|
* summary: Replace session (full update)
|
||||||
* tags: [Sessions]
|
* tags: [Sessions]
|
||||||
@ -743,7 +744,7 @@ const createSessionsRouter = (): express.Router => {
|
|||||||
)
|
)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
* /agents/{agentId}/sessions/{sessionId}:
|
||||||
* patch:
|
* patch:
|
||||||
* summary: Update session (partial update)
|
* summary: Update session (partial update)
|
||||||
* tags: [Sessions]
|
* tags: [Sessions]
|
||||||
@ -795,7 +796,7 @@ const createSessionsRouter = (): express.Router => {
|
|||||||
)
|
)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions/{sessionId}:
|
* /agents/{agentId}/sessions/{sessionId}:
|
||||||
* delete:
|
* delete:
|
||||||
* summary: Delete session
|
* summary: Delete session
|
||||||
* tags: [Sessions]
|
* tags: [Sessions]
|
||||||
@ -834,7 +835,7 @@ const createMessagesRouter = (): express.Router => {
|
|||||||
// Message CRUD routes (nested under agent/session)
|
// Message CRUD routes (nested under agent/session)
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/agents/{agentId}/sessions/{sessionId}/messages:
|
* /agents/{agentId}/sessions/{sessionId}/messages:
|
||||||
* post:
|
* post:
|
||||||
* summary: Create a new message in a session
|
* summary: Create a new message in a session
|
||||||
* tags: [Messages]
|
* tags: [Messages]
|
||||||
@ -902,8 +903,45 @@ const createMessagesRouter = (): express.Router => {
|
|||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: '#/components/schemas/ErrorResponse'
|
* $ref: '#/components/schemas/ErrorResponse'
|
||||||
*/
|
*/
|
||||||
messagesRouter.post('/', validateSessionMessage, handleValidationErrors, messageHandlers.createMessage)
|
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
|
return messagesRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { CreateSessionMessageRequestSchema } from '@types'
|
import { CreateSessionMessageRequestSchema, SessionMessageIdParamSchema } from '@types'
|
||||||
|
|
||||||
import { createZodValidator } from './zodValidator'
|
import { createZodValidator } from './zodValidator'
|
||||||
|
|
||||||
export const validateSessionMessage = createZodValidator({
|
export const validateSessionMessage = createZodValidator({
|
||||||
body: CreateSessionMessageRequestSchema
|
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 { SpanEntity, TokenUsage } from '@mcp-trace/trace-core'
|
||||||
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, UpgradeChannel } from '@shared/config/constant'
|
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, UpgradeChannel } from '@shared/config/constant'
|
||||||
import { IpcChannel } from '@shared/IpcChannel'
|
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 checkDiskSpace from 'check-disk-space'
|
||||||
import { BrowserWindow, dialog, ipcMain, ProxyConfig, session, shell, systemPreferences, webContents } from 'electron'
|
import { BrowserWindow, dialog, ipcMain, ProxyConfig, session, shell, systemPreferences, webContents } from 'electron'
|
||||||
import fontList from 'font-list'
|
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(
|
||||||
try {
|
IpcChannel.AgentMessage_GetHistory,
|
||||||
return await agentMessageRepository.getSessionHistory(sessionId)
|
async (
|
||||||
} catch (error) {
|
_event,
|
||||||
logger.error('Failed to get agent session history', error as Error)
|
{ sessionId }: { sessionId: string }
|
||||||
throw error
|
): Promise<AgentPersistedMessage[]> => {
|
||||||
|
try {
|
||||||
|
return await agentMessageRepository.getSessionHistory(sessionId);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to get agent session history", error as Error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
);
|
||||||
|
|
||||||
//only for mac
|
//only for mac
|
||||||
if (isMac) {
|
if (isMac) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import type {
|
|||||||
ListOptions
|
ListOptions
|
||||||
} from '@types'
|
} from '@types'
|
||||||
import { TextStreamPart } from 'ai'
|
import { TextStreamPart } from 'ai'
|
||||||
import { desc, eq } from 'drizzle-orm'
|
import { and, desc, eq } from 'drizzle-orm'
|
||||||
|
|
||||||
import { BaseService } from '../BaseService'
|
import { BaseService } from '../BaseService'
|
||||||
import { sessionMessagesTable } from '../database/schema'
|
import { sessionMessagesTable } from '../database/schema'
|
||||||
@ -145,6 +145,16 @@ export class SessionMessageService extends BaseService {
|
|||||||
return { messages }
|
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(
|
async createSessionMessage(
|
||||||
session: GetAgentSessionResponse,
|
session: GetAgentSessionResponse,
|
||||||
messageData: CreateSessionMessageRequest,
|
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> {
|
public async updateSession(agentId: string, session: UpdateSessionForm): Promise<GetAgentSessionResponse> {
|
||||||
const url = this.getSessionPaths(agentId).withId(session.id)
|
const url = this.getSessionPaths(agentId).withId(session.id)
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -36,7 +36,13 @@ export const DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS: MessageMenubarButtonId[] = [
|
|||||||
'more-menu'
|
'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>([
|
const messageMenubarRegistry = new Map<MessageMenubarScope, MessageMenubarScopeConfig>([
|
||||||
[DEFAULT_MESSAGE_MENUBAR_SCOPE, { buttonIds: [...DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS] }],
|
[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')
|
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
|
// Query validation schemas
|
||||||
export const PaginationQuerySchema = z.object({
|
export const PaginationQuerySchema = z.object({
|
||||||
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user