- Local variable 'sessions' was shadowing the ref 'sessions' - sessions.value = sessions was not updating the ref - Renamed local variable to 'sessionList' and properly assign to sessions.value - This fixes sessions not appearing in history list after save
273 lines
7.9 KiB
TypeScript
273 lines
7.9 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
|
|
last_query?: string
|
|
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)
|
|
|
|
async function ensureAuthToken() {
|
|
const stored = localStorage.getItem('mb_admin_token')
|
|
if (stored) {
|
|
try {
|
|
const data = JSON.parse(stored)
|
|
if (new Date(data.expires_at) > new Date()) {
|
|
return true
|
|
}
|
|
} catch {}
|
|
}
|
|
// Auto-login with demo credentials
|
|
try {
|
|
const base = getApiBase()
|
|
const resp = await fetch(`${base}/api/v2/admin/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username: 'demo', password: 'demo123' })
|
|
})
|
|
if (resp.ok) {
|
|
const data = await resp.json()
|
|
localStorage.setItem('mb_admin_token', JSON.stringify(data))
|
|
return true
|
|
}
|
|
} catch {}
|
|
return false
|
|
}
|
|
|
|
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 {
|
|
await ensureAuthToken()
|
|
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()
|
|
// Enrich sessions with last query from messages
|
|
const sessionList = data.sessions || []
|
|
for (const s of sessionList) {
|
|
try {
|
|
const msgResp = await fetch(`${base}/api/v2/search-sessions/${s.id}/messages?limit=1`, {
|
|
headers: getAuthHeaders()
|
|
})
|
|
if (msgResp.ok) {
|
|
const msgData = await msgResp.json()
|
|
const lastUserMsg = msgData.messages?.find((m: any) => m.role === 'user')
|
|
if (lastUserMsg) {
|
|
s.last_query = lastUserMsg.content
|
|
}
|
|
}
|
|
} catch {}
|
|
}
|
|
sessions.value = sessionList
|
|
} 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 {
|
|
await ensureAuthToken()
|
|
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 the newly created session from the sessions list
|
|
const newSession = sessions.value.find(s => s.id === data.session?.id)
|
|
return newSession || 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 {
|
|
await ensureAuthToken()
|
|
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 {
|
|
await ensureAuthToken()
|
|
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 {
|
|
await ensureAuthToken()
|
|
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 {
|
|
await ensureAuthToken()
|
|
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 {
|
|
await ensureAuthToken()
|
|
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
|
|
}
|
|
}
|