| import { defineStore } from 'pinia'; |
| import axios from 'axios'; |
|
|
| export const useCommonStore = defineStore({ |
| id: 'common', |
| state: () => ({ |
| |
| eventSource: null, |
| log_cache: [], |
| sse_connected: false, |
|
|
| log_cache_max_len: 1000, |
| startTime: -1, |
|
|
| pluginMarketData: [], |
| }), |
| actions: { |
| async createEventSource() { |
| if (this.eventSource) { |
| return |
| } |
| const controller = new AbortController(); |
| const { signal } = controller; |
| |
| |
| |
| const headers = { |
| 'Content-Type': 'multipart/form-data', |
| 'Authorization': 'Bearer ' + localStorage.getItem('token') |
| }; |
| |
| fetch('/api/live-log', { |
| method: 'GET', |
| headers, |
| signal, |
| cache: 'no-cache', |
| }).then(response => { |
| if (!response.ok) { |
| throw new Error(`SSE connection failed: ${response.status}`); |
| } |
| console.log('SSE stream opened'); |
| this.sse_connected = true; |
|
|
| const reader = response.body.getReader(); |
| const decoder = new TextDecoder(); |
| let bufferedText = ''; |
|
|
| const processStream = ({ done, value }) => { |
| if (done) { |
| console.log('SSE stream closed'); |
| setTimeout(() => { |
| this.eventSource = null; |
| this.createEventSource(); |
| }, 2000); |
| return; |
| } |
|
|
| |
| const text = decoder.decode(value, { stream: true }); |
| bufferedText += text; |
|
|
| |
| const segments = bufferedText.split('\n\n'); |
| bufferedText = segments.pop() || ''; |
|
|
| segments.forEach(segment => { |
| const line = segment.trim(); |
| if (!line.startsWith('data: ')) { |
| return; |
| } |
|
|
| const logLine = line.replace('data: ', '').trim(); |
| if (!logLine) { |
| return; |
| } |
|
|
| try { |
| const logObject = JSON.parse(logLine); |
| |
| |
| if (!logObject.uuid) { |
| if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { |
| logObject.uuid = crypto.randomUUID(); |
| } else { |
| |
| logObject.uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { |
| var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); |
| return v.toString(16); |
| }); |
| } |
| } |
|
|
| this.log_cache.push(logObject); |
| |
| if (this.log_cache.length > this.log_cache_max_len) { |
| this.log_cache.splice(0, this.log_cache.length - this.log_cache_max_len); |
| } |
| } catch (err) { |
| console.warn('Failed to parse SSE log line, skipping:', err, logLine); |
| } |
| }); |
| |
| return reader.read().then(processStream); |
| }; |
|
|
| reader.read().then(processStream); |
| }).catch(error => { |
| console.error('SSE error:', error); |
| |
| this.log_cache.push({ |
| type: 'log', |
| level: 'ERROR', |
| time: Date.now() / 1000, |
| data: 'SSE Connection failed, retrying in 5 seconds...', |
| uuid: 'error-' + Date.now() |
| }); |
| setTimeout(() => { |
| this.eventSource = null; |
| this.createEventSource(); |
| }, 1000); |
| }); |
|
|
| |
| this.eventSource = controller; |
| }, |
| closeEventSourcet() { |
| if (this.eventSource) { |
| this.eventSource.abort(); |
| this.eventSource = null; |
| } |
| }, |
| getLogCache() { |
| return this.log_cache |
| }, |
| async fetchStartTime() { |
| const res = await axios.get('/api/stat/start-time'); |
| this.startTime = res.data.data.start_time; |
| return this.startTime; |
| }, |
| getStartTime() { |
| if (this.startTime !== -1) { |
| return this.startTime |
| } |
| this.fetchStartTime().catch(() => {}); |
| return this.startTime |
| }, |
| async getPluginCollections(force = false, customSource = null) { |
| |
| if (!force && this.pluginMarketData.length > 0 && !customSource) { |
| return Promise.resolve(this.pluginMarketData); |
| } |
|
|
| |
| let url = force ? '/api/plugin/market_list?force_refresh=true' : '/api/plugin/market_list'; |
| if (customSource) { |
| url += (url.includes('?') ? '&' : '?') + `custom_registry=${encodeURIComponent(customSource)}`; |
| } |
|
|
| return axios.get(url) |
| .then((res) => { |
| let data = [] |
| if (res.data.data && typeof res.data.data === 'object') { |
| for (let key in res.data.data) { |
| const pluginData = res.data.data[key]; |
| |
| data.push({ |
| "name": pluginData.name || key, |
| "desc": pluginData.desc, |
| "author": pluginData.author, |
| "repo": pluginData.repo, |
| "installed": false, |
| "version": pluginData?.version ? pluginData.version : "未知", |
| "social_link": pluginData?.social_link, |
| "tags": pluginData?.tags ? pluginData.tags : [], |
| "logo": pluginData?.logo ? pluginData.logo : "", |
| "pinned": pluginData?.pinned ? pluginData.pinned : false, |
| "stars": pluginData?.stars ? pluginData.stars : 0, |
| "updated_at": pluginData?.updated_at ? pluginData.updated_at : "", |
| "display_name": pluginData?.display_name ? pluginData.display_name : "", |
| "astrbot_version": pluginData?.astrbot_version ? pluginData.astrbot_version : "", |
| "support_platforms": Array.isArray(pluginData?.support_platforms) |
| ? pluginData.support_platforms |
| : Array.isArray(pluginData?.support_platform) |
| ? pluginData.support_platform |
| : Array.isArray(pluginData?.platform) |
| ? pluginData.platform |
| : [], |
| }) |
| } |
| } |
| |
| this.pluginMarketData = data; |
| return data; |
| }) |
| .catch((err) => { |
| return Promise.reject(err); |
| }); |
| }, |
| } |
| }); |
|
|