Files
momentry_studio/src/composables/useSearchSession.ts
Momentry Studio fe91afff98 feat: integrate MarkBase Search Session API into Studio
- Created useSearchSession composable for session management
- Added 7 API endpoints in api/index.ts
- Updated SearchView.vue to use sessions instead of history
- Session sidebar with create, rename, delete functionality
- Auto-save messages to current session after each search
- Auto-create default session on first load
- Updated CSS for session active state and new session button
2026-10-27 05:06:03 +08:00

220 lines
6.2 KiB
TypeScript

import { ref } from 'vue'
import { getApiBase } from '@/api/config'
export interface SearchSession {
id: string
title: string
mode: 'keyword' | 'visual' | 'people' | 'agent'
message_count: number
created_at: string
updated_at: string
}
export interface SearchMessage {
id: string
session_id: string
role: 'user' | 'assistant'
content: string
results: string
mode: string
created_at: string
}
export function useSearchSession() {
const sessions = ref<SearchSession[]>([])
const currentSession = ref<SearchSession | null>(null)
const messages = ref<SearchMessage[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
function getAuthHeaders(): Record<string, string> {
const stored = localStorage.getItem('mb_admin_token')
if (stored) {
try {
const data = JSON.parse(stored)
if (new Date(data.expires_at) > new Date()) {
return { 'Authorization': `Bearer ${data.token}` }
}
} catch {}
}
return {}
}
async function loadSessions() {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions`, {
headers: getAuthHeaders()
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const data = await resp.json()
sessions.value = data.sessions || []
} catch (e: any) {
error.value = e.message || 'Failed to load sessions'
} finally {
loading.value = false
}
}
async function createSession(title?: string, mode?: string) {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
body: JSON.stringify({ title: title || '未命名搜尋', mode: mode || 'keyword' })
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const data = await resp.json()
await loadSessions()
return data.session
} catch (e: any) {
error.value = e.message || 'Failed to create session'
return null
} finally {
loading.value = false
}
}
async function renameSession(id: string, title: string) {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
body: JSON.stringify({ title })
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
await loadSessions()
if (currentSession.value?.id === id) {
currentSession.value.title = title
}
} catch (e: any) {
error.value = e.message || 'Failed to rename session'
} finally {
loading.value = false
}
}
async function deleteSession(id: string) {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions/${id}`, {
method: 'DELETE',
headers: getAuthHeaders()
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
if (currentSession.value?.id === id) {
currentSession.value = null
messages.value = []
}
await loadSessions()
} catch (e: any) {
error.value = e.message || 'Failed to delete session'
} finally {
loading.value = false
}
}
async function loadMessages(sessionId: string) {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions/${sessionId}/messages`, {
headers: getAuthHeaders()
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const data = await resp.json()
messages.value = data.messages || []
} catch (e: any) {
error.value = e.message || 'Failed to load messages'
} finally {
loading.value = false
}
}
async function saveMessage(sessionId: string, message: { role: string; content: string; mode?: string; results?: any }) {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions/${sessionId}/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
body: JSON.stringify({
role: message.role,
content: message.content,
mode: message.mode || 'keyword',
results: message.results ? JSON.stringify(message.results) : ''
})
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const data = await resp.json()
await loadMessages(sessionId)
await loadSessions()
return data.message
} catch (e: any) {
error.value = e.message || 'Failed to save message'
return null
} finally {
loading.value = false
}
}
async function updateMessage(sessionId: string, msgId: string, update: { role?: string; content?: string; results?: any }) {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v2/search-sessions/${sessionId}/messages/${msgId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
body: JSON.stringify({
...update,
results: update.results ? JSON.stringify(update.results) : undefined
})
})
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
await loadMessages(sessionId)
} catch (e: any) {
error.value = e.message || 'Failed to update message'
} finally {
loading.value = false
}
}
function setCurrentSession(session: SearchSession | null) {
currentSession.value = session
if (session) {
loadMessages(session.id)
} else {
messages.value = []
}
}
return {
sessions,
currentSession,
messages,
loading,
error,
loadSessions,
createSession,
renameSession,
deleteSession,
loadMessages,
saveMessage,
updateMessage,
setCurrentSession
}
}