chore: full stack stability and migration fixes, plus react UI progress
This commit is contained in:
21
control-plane-ui/src/hooks/useDatabase.ts
Normal file
21
control-plane-ui/src/hooks/useDatabase.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { apiService, DbTable } from '../services/api'
|
||||
|
||||
export function useDatabase() {
|
||||
const tablesQuery = useQuery({
|
||||
queryKey: ['tables'],
|
||||
queryFn: () => apiService.getTables().then(res => res.data),
|
||||
})
|
||||
|
||||
const useTableData = (schema: string | null, name: string | null) => useQuery({
|
||||
queryKey: ['tableData', schema, name],
|
||||
queryFn: () => (schema && name) ? apiService.getTableData(schema, name).then(res => res.data) : Promise.resolve([]),
|
||||
enabled: !!(schema && name),
|
||||
})
|
||||
|
||||
return {
|
||||
tables: tablesQuery.data || [],
|
||||
isLoadingTables: tablesQuery.isLoading,
|
||||
useTableData,
|
||||
}
|
||||
}
|
||||
26
control-plane-ui/src/hooks/useFunctions.ts
Normal file
26
control-plane-ui/src/hooks/useFunctions.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { apiService, EdgeFunction } from '../services/api'
|
||||
|
||||
export function useFunctions() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const functionsQuery = useQuery({
|
||||
queryKey: ['functions'],
|
||||
queryFn: () => apiService.getFunctions().then(res => res.data),
|
||||
})
|
||||
|
||||
const deployFunctionMutation = useMutation({
|
||||
mutationFn: (data: { name: string; runtime: string; code_base64: string }) =>
|
||||
apiService.deployFunction(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['functions'] })
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
functions: functionsQuery.data || [],
|
||||
isLoadingFunctions: functionsQuery.isLoading,
|
||||
deployFunction: deployFunctionMutation.mutate,
|
||||
isDeploying: deployFunctionMutation.isPending,
|
||||
}
|
||||
}
|
||||
18
control-plane-ui/src/hooks/useLogs.ts
Normal file
18
control-plane-ui/src/hooks/useLogs.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { apiService } from '../services/api'
|
||||
|
||||
export function useLogs(query: string = '', limit: number = 50) {
|
||||
const logsQuery = useQuery({
|
||||
queryKey: ['logs', query, limit],
|
||||
queryFn: () => apiService.getLogs({ query, limit }).then(res => res.data),
|
||||
refetchInterval: 10000, // Poll every 10s
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
return {
|
||||
logs: logsQuery.data || [],
|
||||
isLoading: logsQuery.isLoading,
|
||||
isRefetching: logsQuery.isRefetching,
|
||||
refetch: logsQuery.refetch,
|
||||
}
|
||||
}
|
||||
33
control-plane-ui/src/hooks/usePillars.ts
Normal file
33
control-plane-ui/src/hooks/usePillars.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { apiService } from '../services/api'
|
||||
|
||||
export interface PillarStats {
|
||||
pillar: string
|
||||
node_count: number
|
||||
active_count: number
|
||||
is_scaling: boolean
|
||||
metrics?: {
|
||||
cpu_usage?: number
|
||||
memory_usage?: number
|
||||
request_rate?: number
|
||||
}
|
||||
suggestion?: {
|
||||
action: 'scale_up' | 'scale_down' | 'none'
|
||||
reason: string
|
||||
target_count: number
|
||||
}
|
||||
}
|
||||
|
||||
export function usePillars() {
|
||||
const pillarsQuery = useQuery({
|
||||
queryKey: ['pillars'],
|
||||
queryFn: () => apiService.getPillars().then(res => res.data),
|
||||
refetchInterval: 5000, // Refresh every 5s for live scaling status
|
||||
})
|
||||
|
||||
return {
|
||||
pillars: (pillarsQuery.data as PillarStats[]) || [],
|
||||
isLoading: pillarsQuery.isLoading,
|
||||
error: pillarsQuery.error,
|
||||
}
|
||||
}
|
||||
82
control-plane-ui/src/hooks/useRealtime.ts
Normal file
82
control-plane-ui/src/hooks/useRealtime.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
|
||||
export interface RealtimeMessage {
|
||||
id: string
|
||||
timestamp: string
|
||||
type: 'IN' | 'OUT' | 'SYS'
|
||||
payload: any
|
||||
channel?: string
|
||||
}
|
||||
|
||||
export function useRealtime() {
|
||||
const [messages, setMessages] = useState<RealtimeMessage[]>([])
|
||||
const [isConnected, setIsConnected] = useState(false)
|
||||
const ws = useRef<WebSocket | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
// Determine WS URL based on current host
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
const host = window.location.host
|
||||
const url = `${protocol}//${host}/realtime/v1`
|
||||
|
||||
const connect = () => {
|
||||
ws.current = new WebSocket(url)
|
||||
|
||||
ws.current.onopen = () => {
|
||||
setIsConnected(true)
|
||||
addMessage({ type: 'SYS', payload: 'Connected to Realtime Gateway' })
|
||||
}
|
||||
|
||||
ws.current.onclose = () => {
|
||||
setIsConnected(false)
|
||||
addMessage({ type: 'SYS', payload: 'Disconnected from Realtime Gateway. Retrying...' })
|
||||
setTimeout(connect, 3000)
|
||||
}
|
||||
|
||||
ws.current.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data)
|
||||
addMessage({ type: 'IN', payload: data })
|
||||
} catch (e) {
|
||||
addMessage({ type: 'IN', payload: event.data })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const addMessage = (msg: Omit<RealtimeMessage, 'id' | 'timestamp'>) => {
|
||||
setMessages(prev => [
|
||||
{
|
||||
...msg,
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
...prev.slice(0, 99) // Keep last 100 messages
|
||||
])
|
||||
}
|
||||
|
||||
connect()
|
||||
|
||||
return () => {
|
||||
if (ws.current) ws.current.close()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const sendMessage = (payload: any) => {
|
||||
if (ws.current && isConnected) {
|
||||
ws.current.send(JSON.stringify(payload))
|
||||
setMessages(prev => [
|
||||
{
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'OUT',
|
||||
payload
|
||||
},
|
||||
...prev
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const clearMessages = () => setMessages([])
|
||||
|
||||
return { messages, isConnected, sendMessage, clearMessages }
|
||||
}
|
||||
33
control-plane-ui/src/hooks/useStorage.ts
Normal file
33
control-plane-ui/src/hooks/useStorage.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { apiService, Bucket, StorageObject } from '../services/api'
|
||||
|
||||
export function useStorage() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const bucketsQuery = useQuery({
|
||||
queryKey: ['buckets'],
|
||||
queryFn: () => apiService.getBuckets().then(res => res.data),
|
||||
})
|
||||
|
||||
const useObjects = (bucketId: string | null) => useQuery({
|
||||
queryKey: ['objects', bucketId],
|
||||
queryFn: () => bucketId ? apiService.getBucketObjects(bucketId).then(res => res.data) : Promise.resolve([]),
|
||||
enabled: !!bucketId,
|
||||
})
|
||||
|
||||
const deleteObjectMutation = useMutation({
|
||||
mutationFn: ({ bucketId, name }: { bucketId: string; name: string }) =>
|
||||
apiService.deleteObject(bucketId, name),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['objects', variables.bucketId] })
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
buckets: bucketsQuery.data || [],
|
||||
isLoadingBuckets: bucketsQuery.isLoading,
|
||||
useObjects,
|
||||
deleteObject: deleteObjectMutation.mutate,
|
||||
isDeletingObject: deleteObjectMutation.isPending,
|
||||
}
|
||||
}
|
||||
26
control-plane-ui/src/hooks/useUsers.ts
Normal file
26
control-plane-ui/src/hooks/useUsers.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { apiService, AdminUser } from '../services/api'
|
||||
|
||||
export function useUsers() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const usersQuery = useQuery({
|
||||
queryKey: ['users'],
|
||||
queryFn: () => apiService.getUsers().then(res => res.data),
|
||||
})
|
||||
|
||||
const deleteUserMutation = useMutation({
|
||||
mutationFn: (id: string) => apiService.deleteUser(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['users'] })
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
users: usersQuery.data || [],
|
||||
isLoading: usersQuery.isLoading,
|
||||
error: usersQuery.error,
|
||||
deleteUser: deleteUserMutation.mutate,
|
||||
isDeleting: deleteUserMutation.isPending,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user