chore: full stack stability and migration fixes, plus react UI progress
Some checks failed
CI / podman-build (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-03-18 09:01:38 +02:00
parent 38cab8c246
commit a66d908eff
142 changed files with 12210 additions and 3402 deletions

View 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,
}
}

View 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,
}
}

View 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,
}
}

View 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,
}
}

View 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 }
}

View 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,
}
}

View 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,
}
}