diff --git a/api/.gitignore b/api/.gitignore index cbbcf24a..3405ef1f 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -1,2 +1,3 @@ tmp/ -api/chatgpt_backend \ No newline at end of file +api/chatgpt_backend +env.sh \ No newline at end of file diff --git a/api/Makefile b/api/Makefile index 656eb75e..eb198e6b 100644 --- a/api/Makefile +++ b/api/Makefile @@ -19,13 +19,6 @@ build: vet go build .PHONY: build - -export OPENAI_API_KEY=sk-KltHM7dsS8x2oL0KGJ69XXX -export PG_HOST=192.168.0.1 -export PG_DB=hwu -export PG_USER=hwu -export PG_PASS=123456 -export PG_PORT=5432 serve: @echo "Starting server..." diff --git a/api/chat_message_handler.go b/api/chat_message_handler.go index 751ecc21..4d29b3d7 100644 --- a/api/chat_message_handler.go +++ b/api/chat_message_handler.go @@ -33,6 +33,7 @@ func (h *ChatMessageHandler) Register(router *mux.Router) { router.HandleFunc("/uuid/chat_messages/{uuid}", h.UpdateChatMessageByUUID).Methods(http.MethodPut) router.HandleFunc("/uuid/chat_messages/{uuid}", h.DeleteChatMessageByUUID).Methods(http.MethodDelete) router.HandleFunc("/uuid/chat_messages/chat_sessions/{uuid}", h.GetChatHistoryBySessionUUID).Methods(http.MethodGet) + router.HandleFunc("/uuid/chat_messages/chat_sessions/{uuid}", h.DeleteChatMessagesBySesionUUID).Methods(http.MethodDelete) } @@ -207,3 +208,14 @@ func (h *ChatMessageHandler) GetChatHistoryBySessionUUID(w http.ResponseWriter, } json.NewEncoder(w).Encode(simple_msgs) } + +// DeleteChatMessagesBySesionUUID delete chat messages by session uuid +func (h *ChatMessageHandler) DeleteChatMessagesBySesionUUID(w http.ResponseWriter, r *http.Request) { + uuidStr := mux.Vars(r)["uuid"] + err := h.service.DeleteChatMessagesBySesionUUID(r.Context(), uuidStr) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) +} \ No newline at end of file diff --git a/api/chat_message_service.go b/api/chat_message_service.go index 2557bcbd..5a2d6a10 100644 --- a/api/chat_message_service.go +++ b/api/chat_message_service.go @@ -201,3 +201,12 @@ func (s *ChatMessageService) GetLastNChatMessages(ctx context.Context, uuid stri } return message, nil } + +//DeleteChatMessagesBySesionUUID deletes chat messages by session uuid. +func (s *ChatMessageService) DeleteChatMessagesBySesionUUID(ctx context.Context, uuid string) error { + err := s.q.DeleteChatMessagesBySesionUUID(ctx, uuid) + if err != nil { + return errors.New("failed to delete message") + } + return nil +} \ No newline at end of file diff --git a/api/sqlc/queries/chat_message.sql b/api/sqlc/queries/chat_message.sql index cd691fc6..6fd75c90 100644 --- a/api/sqlc/queries/chat_message.sql +++ b/api/sqlc/queries/chat_message.sql @@ -103,4 +103,9 @@ ORDER BY created_at; -- name: UpdateChatMessageContent :exec UPDATE chat_message SET content = $2, updated_at = now() -WHERE uuid = $1 ; \ No newline at end of file +WHERE uuid = $1 ; + + +-- name: DeleteChatMessagesBySesionUUID :exec +DELETE FROM chat_message +WHERE chat_session_uuid = $1; \ No newline at end of file diff --git a/api/sqlc_queries/chat_message.sql.go b/api/sqlc_queries/chat_message.sql.go index 60cd155d..bbfae5b9 100644 --- a/api/sqlc_queries/chat_message.sql.go +++ b/api/sqlc_queries/chat_message.sql.go @@ -124,6 +124,16 @@ func (q *Queries) DeleteChatMessageByUUID(ctx context.Context, uuid string) erro return err } +const deleteChatMessagesBySesionUUID = `-- name: DeleteChatMessagesBySesionUUID :exec +DELETE FROM chat_message +WHERE chat_session_uuid = $1 +` + +func (q *Queries) DeleteChatMessagesBySesionUUID(ctx context.Context, chatSessionUuid string) error { + _, err := q.db.ExecContext(ctx, deleteChatMessagesBySesionUUID, chatSessionUuid) + return err +} + const getAllChatMessages = `-- name: GetAllChatMessages :many SELECT id, uuid, chat_session_uuid, role, content, score, user_id, created_at, updated_at, created_by, updated_by, raw FROM chat_message ORDER BY id ` diff --git a/e2e/tests/01_register.spec.ts b/e2e/tests/01_register.spec.ts index c66a9504..ae7631bb 100644 --- a/e2e/tests/01_register.spec.ts +++ b/e2e/tests/01_register.spec.ts @@ -14,6 +14,5 @@ test('test', async ({ page }) => { await page.getByTestId('password').locator('input').click(); await page.getByTestId('password').locator('input').fill('@WuHao5'); await page.getByTestId('signup').click(); - }); diff --git a/e2e/tests/06_clear_messages.spec.ts b/e2e/tests/06_clear_messages.spec.ts new file mode 100644 index 00000000..8fb11065 --- /dev/null +++ b/e2e/tests/06_clear_messages.spec.ts @@ -0,0 +1,61 @@ +import { test, expect } from '@playwright/test'; +import { Pool } from 'pg'; +import { selectUserByEmail } from '../lib/db/user'; +import { selectChatSessionByUserId as selectChatSessionsByUserId } from '../lib/db/chat_session'; +import { selectChatPromptsBySessionUUID } from '../lib/db/chat_prompt'; +import { selectChatMessagesBySessionUUID } from '../lib/db/chat_message'; +import { randomEmail } from '../lib/sample'; +import { db_config } from '../lib/db/config'; + +const pool = new Pool(db_config); + +const test_email = randomEmail(); + +test('after clear conversation, only system message remains', async ({ page }) => { + await page.goto('/'); + await page.getByTestId('email').click(); + await page.getByTestId('email').locator('input').fill(test_email); + await page.getByTestId('password').locator('input').click(); + await page.getByTestId('password').locator('input').fill('@WuHao5'); + await page.getByTestId('signup').click(); + // sleep 1 second + await page.waitForTimeout(1000); + let input_area = await page.$("#message_textarea textarea") + await input_area?.click(); + await input_area?.fill('test_demo_bestqa'); + await input_area?.press('Enter'); + await page.waitForTimeout(1000); + await input_area?.fill('test_demo_bestqa'); + await input_area?.press('Enter'); + // get message counts in the conversation + + await page.waitForTimeout(1000); + + const message_counts = await page.$$eval('.message-text', (messages) => messages.length); + expect(message_counts).toBe(4); + + const user = await selectUserByEmail(pool, test_email); + expect(user.email).toBe(test_email); + // expect(user.id).toBe(37); + const sessions = await selectChatSessionsByUserId(pool, user.id); + const session = sessions[0]; + + // clear + await page.getByRole('contentinfo').getByRole('button').first().click(); + await page.getByRole('button', { name: '是' }).click(); + + // sleep 500 ms + await page.waitForTimeout(500); + // get message counts in the conversation + const message_count_after_clear = await page.$$eval('.message-text', (messages) => messages.length); + expect(message_count_after_clear).toBe(1); + + const prompts = await selectChatPromptsBySessionUUID(pool, session.uuid) + expect(prompts.length).toBe(1); + expect(prompts[0].updated_by).toBe(user.id); + // sleep 5 seconds + await page.waitForTimeout(500); + const messages = await selectChatMessagesBySessionUUID(pool, session.uuid) + expect(messages.length).toBe(0); +}); + diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 994552e0..07c34e99 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -157,6 +157,18 @@ export const renameChatSession = async (uuid: string, name: string) => { } } +export const clearSessionChatMessages = async (sessionUuid: string) => { + try { + const response = await request.delete(`/uuid/chat_messages/chat_sessions/${sessionUuid}`) + return response.data + } + catch (error) { + console.error(error) + throw error + } +} + + export const deleteChatMessage = async (uuid: string) => { try { const response = await request.delete(`/uuid/chat_messages/${uuid}`) diff --git a/web/src/locales/zh-CN.ts b/web/src/locales/zh-CN.ts index 9a0b6481..e7db85d2 100644 --- a/web/src/locales/zh-CN.ts +++ b/web/src/locales/zh-CN.ts @@ -1,5 +1,6 @@ export default { common: { + help: '第一条是主题(prompt), 上下午包括10条信息', edit: '编辑', delete: '删除', save: '保存', diff --git a/web/src/store/modules/chat/index.ts b/web/src/store/modules/chat/index.ts index ed0dc14f..a5525125 100644 --- a/web/src/store/modules/chat/index.ts +++ b/web/src/store/modules/chat/index.ts @@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid' import { getLocalState, setLocalState } from './helper' import { router } from '@/router' import { + clearSessionChatMessages, createChatSession, createOrUpdateUserActiveChatSession, deleteChatData, @@ -237,6 +238,7 @@ export const useChatStore = defineStore('chat-store', { }, clearChatByUuid(uuid: string) { + // does this every happen? if (!uuid) { if (this.chat.length) { this.chat[0].data = [] @@ -247,7 +249,8 @@ export const useChatStore = defineStore('chat-store', { const index = this.chat.findIndex(item => item.uuid === uuid) if (index !== -1) { - this.chat[index].data = [] + this.chat[index].data = this.chat[index].data.slice(0, 1) + clearSessionChatMessages(uuid) this.recordState() } }, diff --git a/web/src/views/chat/components/Message/index.vue b/web/src/views/chat/components/Message/index.vue index 4bd55de6..4796cd2a 100644 --- a/web/src/views/chat/components/Message/index.vue +++ b/web/src/views/chat/components/Message/index.vue @@ -95,6 +95,7 @@ function handleRegenerate() { > {