RSS Git Download  Clone
Raw Blame History 8kB 228 lines
import { defineStore } from 'pinia'
import { request } from './socket.service'
import { useCommonStore } from './common.store'
import { useRedisStateStore } from './redis-state.store'
import { useSettingsStore } from './settings.store'
import { useI18nStore } from './i18n.store'
import { useOverlayStore } from './overlay.store'
import { navigateTo } from './navigation.store'
import { parseRedisInfo } from './redis-parser'

// --- Event bus for component communication ---
type Callback = (...args: any[]) => void
type VoidCallback = () => void
const cmdListeners: Record<string, Set<Callback>> = {}

export function onCommandEvent(event: string, cb: Callback): VoidCallback {
    if (!cmdListeners[event]) cmdListeners[event] = new Set()
    cmdListeners[event].add(cb)
    return () => { cmdListeners[event].delete(cb) }
}

export function emitCommand(event: string, data?: any) {
    cmdListeners[event]?.forEach(cb => cb(data))
}

let lastRefreshAt = 0

export const useMainCommandStore = defineStore('mainCommand', () => {
    async function connect(conn: any) {
        const cloned = JSON.parse(JSON.stringify(conn))
        const settings = useSettingsStore()
        const overlay = useOverlayStore()
        const common = useCommonStore()
        try {
            const dbStorageKey = settings.getStorageKeyCurrentDatabase(cloned.id)
            let db: string | undefined
            try { db = localStorage.getItem(dbStorageKey) ?? undefined } catch {}

            if (cloned.askAuth === true) {
                try {
                    const a = await common.askAuth()
                    cloned.username = a.username || undefined
                    cloned.password = a.password || undefined
                } catch { return }
            }

            const i18n = useI18nStore()
            overlay.show({ message: i18n.strings?.title?.connectingRedis })

            useRedisStateStore().connectionState = 'connecting'

            const response = await request({
                action: 'connection/connect',
                payload: { connection: cloned, db },
            })

            const redisState = useRedisStateStore()
            const databaseIndexes: number[] = []
            let idx = 0
            while (idx < response.databases) databaseIndexes.push(idx++)
            const commands: string[] = []
            Object.keys(response.commands ?? {}).forEach(k => commands.push(response.commands[k][0]))
            commands.sort()
            const modules = Array.isArray(response.modules) ? response.modules : []

            redisState.page = 1
            redisState.monitor = false
            redisState.dbsize = response.dbsize
            redisState.databaseIndexes = databaseIndexes
            redisState.connection = cloned
            redisState.commands = commands
            redisState.commandsMeta = response.commandsMeta ?? {}
            redisState.modules = modules
            redisState.hasReJSON = modules.some((m: any) => m.name === 'ReJSON')
            redisState.hasRediSearch = modules.some((m: any) => m.name === 'search')
            redisState.hasTimeSeries = modules.some((m: any) => m.name === 'timeseries' || m.name === 'Timeseries')
            redisState.hasBloom = modules.some((m: any) => m.name === 'bf')

            common.loadRedisInfoResponse({ response })
            try { localStorage.setItem(settings.connectInfoStorageKey, JSON.stringify(cloned)) } catch {}

            useRedisStateStore().connectionState = 'connected'
        } catch (error) {
            const settings = useSettingsStore()
            try { localStorage.removeItem(settings.connectInfoStorageKey) } catch {}
            const rs = useRedisStateStore()
            rs.connection = undefined
            rs.connectionState = 'none'
            common.generalHandleError(error)
        } finally {
            overlay.hide()
        }
    }

    async function selectDatabase(dbIndex: number) {
        const redisState = useRedisStateStore()
        const settings = useSettingsStore()

        redisState.currentDatabase = dbIndex
        redisState.page = 1
        redisState.redisChanged = true

        try {
            await request({ action: 'redis/console', payload: { command: `select ${dbIndex}` } })
            const i18n = useI18nStore()
            const s = i18n.strings
            const fn = s?.status?.dbChanged
            const msg = typeof fn === 'function' ? fn({ db: dbIndex }) : ''
            const common = useCommonStore()
            if (msg) common.toast(msg)

            // Persist
            const storageKey = settings.getStorageKeyCurrentDatabase(redisState.connection?.id)
            if (storageKey) try { localStorage.setItem(storageKey, String(dbIndex)) } catch {}

            await refresh({ force: true })
        } catch (e) {
            useCommonStore().generalHandleError(e)
        }
    }

    async function save() {
        try {
            const response = await request({ action: 'redis/save' })
            const info = parseRedisInfo(response.info)
            const redisState = useRedisStateStore()
            redisState.info = info
            const i18n = useI18nStore()
            const s = i18n.strings
            useCommonStore().toast(s?.status?.savedRedis)
        } catch (e) {
            useCommonStore().generalHandleError(e)
        }
    }

    async function statistics() {
        try {
            navigateTo('database.statistics')
            await refresh({ force: true })
        } catch (e) {
            useCommonStore().generalHandleError(e)
        }
    }

    async function refresh(options: { withoutParent?: boolean; force?: boolean } = {}) {
        const now = Date.now()
        if (!options.force && now - lastRefreshAt < 2000) return
        lastRefreshAt = now

        try {
            const redisState = useRedisStateStore()
            const settings = useSettingsStore()
            const payload: any = {}

            const searchValue = redisState.search
            if (!settings.searchClientSide && typeof searchValue === 'string' && searchValue.length > 0) {
                payload.match = settings.searchStartsWith ? searchValue + '*' : '*' + searchValue + '*'
            }

            const response = await request({ action: 'redis/refresh', payload })
            redisState.dbsize = response.dbsize
            redisState.redisChanged = true
            useCommonStore().loadRedisInfoResponse({ response })

            emitCommand('tree-refresh')
            if (!options.withoutParent) emitCommand('refresh-key')
        } catch (e) {
            useCommonStore().generalHandleError(e)
        }
    }

    async function disconnect() {
        const redisState = useRedisStateStore()
        const settings = useSettingsStore()

        try { localStorage.removeItem(settings.connectInfoStorageKey) } catch {}
        redisState.connection = undefined
        redisState.connectionState = 'none'
        redisState.redisConnections = {}
        redisState.monitor = false

        try {
            await request({
                action: 'connection/disconnect',
                payload: { connectionId: redisState.connection?.id },
            })
        } catch {}

        navigateTo('settings')
    }

    function addKey(options: { event: Event; node?: any }) {
        options.event.stopPropagation()
        emitCommand('key-new', options)
    }

    function getCurrentDatabase(): number {
        const redisState = useRedisStateStore()
        const settings = useSettingsStore()
        let db: any = redisState.currentDatabase
        if (db === undefined) {
            const storageKey = settings.getStorageKeyCurrentDatabase(redisState.connection?.id)
            if (storageKey) try { db = localStorage.getItem(storageKey) } catch {}
        }
        return Number(db ?? 0)
    }

    function setCurrentDatabase(value: number) {
        const redisState = useRedisStateStore()
        const settings = useSettingsStore()
        redisState.currentDatabase = value
        const storageKey = settings.getStorageKeyCurrentDatabase(redisState.connection?.id)
        if (storageKey) try { localStorage.setItem(storageKey, String(value)) } catch {}
    }

    return {
        connect,
        selectDatabase,
        save,
        statistics,
        refresh,
        disconnect,
        addKey,
        getCurrentDatabase,
        setCurrentDatabase,
    }
})