import { create } from 'zustand'; import { queryClient, config } from '../../modules/query' import { NocoApi } from '@kevisual/noco' import { toast } from 'react-toastify' import { MyCache } from '@kevisual/cache' const cache = new MyCache('hot_api') type CacheCustom = Array<{ namespace: string; items: CardItem[]; }> const cacheCustom = new MyCache('hot_api_customize'); // --- Types --- export interface CardItem { id: string; title: string; iconUrl?: string; description?: string; data?: any; sort?: number; namespace?: string; } export interface StoreState { items: CardItem[]; isLoading: boolean; machineId?: string; fetchItems: (refresh?: boolean) => Promise; sendEvent: (item: CardItem) => Promise; namespace?: string; setNamespace?: (namespace: string) => void; customizeItems?: CardItem[]; setCustomizeItems?: (items: CardItem[]) => void; getCustomizeItems: (namespace?: string) => Promise; initCustomizeItems?: () => Promise; getMachineId?: () => Promise; setCacheData?: (cacheCustom: CacheCustom) => void; getCacheData?: () => Promise; exportCacheData?: () => Promise; importCacheData?: (jsonData: string) => Promise<{ success: boolean; importedNamespaces?: string[]; message: string; }>; nocoApi?: NocoApi; } // --- Store --- export const useStore = create((set, get) => ({ items: [], isLoading: false, getMachineId: async () => { let machineId = get().machineId; if (machineId) { return machineId; } const res = await queryClient.get({ path: 'config', key: 'getId' }); if (res.code === 200) { machineId = res.data; set({ machineId }); return machineId!; } return ''; }, fetchItems: async (refresh?: boolean) => { set({ isLoading: true }); get().initCustomizeItems?.(); get().getMachineId?.(); const chacheData = await cache.getData().catch(() => null); if (!refresh && chacheData && Array.isArray(chacheData) && chacheData.length > 0) { set({ items: chacheData, isLoading: false }); return; } const life = await config.getConfig('life.json').catch(() => null); if (!life) return; const baseId = life?.baseId || ''; const nocoToken = life?.token || ''; const baseURL = life?.baseURL || ''; if (!baseId || !nocoToken || !baseURL) { toast.error('未配置 nocodb 的 baseId 或 token,请前往设置页面配置'); return }; const nocoApi = new NocoApi({ baseURL, token: nocoToken }); const table = await nocoApi.getTableByName('控制中枢', baseId) if (!table) { toast.error('未找到 nocodb 中的 控制中枢 表,请检查配置'); return; } nocoApi.record.table = table.id set({ nocoApi }); const res = await nocoApi.record.list({ page: 1, limit: 10000, where: "(类型,neq,文档)", }) console.log('Fetched records:', res); if (res.code === 200) { let items: CardItem[] = res.data.list.map((record: any) => ({ id: record.Id, title: record['标题'] || '未命名', iconUrl: record['图标'] || `https://api.dicebear.com/9.x/bottts/svg?seed=${record.Id}`, description: record['总结'] || '', data: record['数据'] || {}, sort: 0, })); cache.setData(items, { expireTime: 30 * 24 * 60 * 60 * 1000 }); // Cache for 10 days set({ items, isLoading: false }); } else { toast.error('获取控制中枢数据失败,请检查配置'); set({ isLoading: false }); return; } }, sendEvent: async (item: CardItem) => { console.log('Sending event for item:', item); if (item.data?.type === 'hotkeys') { const res = await queryClient.post({ path: 'key-sender', keys: item?.data?.hotkeys }); console.log('Event sent for item:', item, 'Response:', res); if (res.code !== 200) { toast.error('事件发送失败'); } return; } toast.info('暂不支持该类型的控制中枢操作'); }, namespace: localStorage.getItem('hotapi-namespace') || 'default', customizeItems: [], setCustomizeItems: (items: CardItem[]) => { set({ customizeItems: items }); // Save to cache asynchronously without blocking const ns = useStore.getState().namespace || 'default'; (async () => { const chacheData = await cacheCustom.getData().catch(() => null); let allData: CacheCustom = chacheData || []; // Update or add the namespace data const existingIndex = allData.findIndex(c => c.namespace === ns); if (existingIndex >= 0) { allData[existingIndex].items = items; } else { allData.push({ namespace: ns, items }); } await cacheCustom.setData(allData, { expireTime: 365 * 24 * 60 * 60 * 1000 }); // Cache for 1 year })(); }, setNamespace: (namespace: string) => { set({ namespace }) get().getCustomizeItems(namespace); localStorage.setItem('hotapi-namespace', namespace); }, getAllNamespaces: async () => { const chacheData = await cacheCustom.getData().catch(() => null); if (chacheData) { return chacheData.map(c => c.namespace); } return ['default']; }, getCustomizeItems: async (namespace?: string) => { const ns = namespace || useStore.getState().namespace || 'default'; const chacheData = await cacheCustom.getData().catch(() => null); if (chacheData) { const nsData = chacheData.find(c => c.namespace === ns); if (nsData) { set({ customizeItems: nsData.items }); return nsData.items; } else { set({ customizeItems: [] }); return []; } } return []; }, initCustomizeItems: async () => { const ns = useStore.getState().namespace || 'default'; await get().getCustomizeItems(ns); }, getCacheData: async () => { const chacheData = await cacheCustom.getData().catch(() => null); return chacheData; }, setCacheData: (cacheCustomData: CacheCustom) => { cacheCustom.setData(cacheCustomData, { expireTime: 365 * 24 * 60 * 60 * 1000 }); // Cache for 1 year }, exportCacheData: async () => { const cacheData = await get().getCacheData?.(); if (cacheData) { const exportData = { version: '1.0.0', exportTime: new Date().toISOString(), data: cacheData }; return JSON.stringify(exportData, null, 2); } return null; }, importCacheData: async (jsonData: string) => { try { const importData = JSON.parse(jsonData); // 验证导入的数据格式 if (!importData.data || !Array.isArray(importData.data)) { throw new Error('导入数据格式不正确'); } // 验证每个命名空间的数据格式 for (const nsData of importData.data) { if (!nsData.namespace || !Array.isArray(nsData.items)) { throw new Error('命名空间数据格式不正确'); } } // 导入数据 get().setCacheData?.(importData.data); // 刷新当前命名空间的数据 const currentNs = get().namespace || 'default'; get().getCustomizeItems?.(currentNs); return { success: true, importedNamespaces: importData.data.map((ns: any) => ns.namespace), message: '数据导入成功' }; } catch (error) { return { success: false, message: error instanceof Error ? error.message : '导入失败' }; } }, }));