fix: Resolve Docker build failures for frontend and backend

Backend fixes:
- Updated psutil from 5.9.8 to 6.1.0 for better ARM64 support and pre-built wheels

Frontend fixes:
- Created missing lib/api/client.ts with axios API client
- Created missing lib/hooks/useStats.ts with system statistics hooks
- Created missing lib/hooks/useBots.ts with bot management hooks
- Fixed .gitignore to not ignore frontend/lib/ directory

These changes resolve the build errors:
- Backend: psutil compilation failure on aarch64
- Frontend: Missing module @/lib/hooks/useStats
This commit is contained in:
Claude
2025-11-21 12:05:24 +00:00
parent af872a2935
commit 82b56cf209
5 changed files with 281 additions and 2 deletions

2
.gitignore vendored
View File

@@ -10,7 +10,7 @@ dist/
downloads/
eggs/
.eggs/
lib/
backend/lib/
lib64/
parts/
sdist/

View File

@@ -7,7 +7,7 @@ pydantic-settings==2.1.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.9
psutil==5.9.8
psutil==6.1.0
aiosqlite==0.19.0
websockets==12.0
python-dotenv==1.0.1

View File

@@ -0,0 +1,48 @@
/**
* API client for making requests to the backend.
*/
import axios from "axios";
import type { ApiResponse } from "@/types/api";
const API_BASE_URL =
process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
export const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
"Content-Type": "application/json",
},
timeout: 10000,
});
// Request interceptor for adding auth token
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem("access_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Response interceptor for handling errors
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Handle token refresh or redirect to login
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
// Optionally redirect to login page
// window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default apiClient;

View File

@@ -0,0 +1,181 @@
/**
* Hooks for fetching and managing bots.
*/
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import apiClient from "@/lib/api/client";
import type {
Bot,
BotListResponse,
BotCreate,
BotUpdate,
BotStatusResponse,
} from "@/types/bot";
import type { BotFilters } from "@/types/api";
/**
* Hook to fetch list of bots with optional filters.
*/
export function useBots(filters?: BotFilters) {
return useQuery({
queryKey: ["bots", filters],
queryFn: async () => {
const params = new URLSearchParams();
if (filters?.status) {
params.append("status", filters.status);
}
if (filters?.search) {
params.append("search", filters.search);
}
if (filters?.page) {
params.append("page", filters.page.toString());
}
if (filters?.page_size) {
params.append("page_size", filters.page_size.toString());
}
const response = await apiClient.get<BotListResponse>(
`/bots?${params.toString()}`
);
return response.data;
},
refetchInterval: 10000, // Refetch every 10 seconds
});
}
/**
* Hook to fetch a single bot by ID.
*/
export function useBot(botId: string) {
return useQuery({
queryKey: ["bots", botId],
queryFn: async () => {
const response = await apiClient.get<Bot>(`/bots/${botId}`);
return response.data;
},
enabled: !!botId,
});
}
/**
* Hook to fetch bot status.
*/
export function useBotStatus(botId: string) {
return useQuery({
queryKey: ["bots", botId, "status"],
queryFn: async () => {
const response = await apiClient.get<BotStatusResponse>(
`/bots/${botId}/status`
);
return response.data;
},
refetchInterval: 5000,
enabled: !!botId,
});
}
/**
* Hook to create a new bot.
*/
export function useCreateBot() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (botData: BotCreate) => {
const response = await apiClient.post<Bot>("/bots", botData);
return response.data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["bots"] });
},
});
}
/**
* Hook to update a bot.
*/
export function useUpdateBot(botId: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (botData: BotUpdate) => {
const response = await apiClient.put<Bot>(`/bots/${botId}`, botData);
return response.data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["bots", botId] });
queryClient.invalidateQueries({ queryKey: ["bots"] });
},
});
}
/**
* Hook to delete a bot.
*/
export function useDeleteBot() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (botId: string) => {
await apiClient.delete(`/bots/${botId}`);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["bots"] });
},
});
}
/**
* Hook to start a bot.
*/
export function useStartBot() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (botId: string) => {
const response = await apiClient.post<Bot>(`/bots/${botId}/start`);
return response.data;
},
onSuccess: (_, botId) => {
queryClient.invalidateQueries({ queryKey: ["bots", botId] });
queryClient.invalidateQueries({ queryKey: ["bots"] });
},
});
}
/**
* Hook to stop a bot.
*/
export function useStopBot() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (botId: string) => {
const response = await apiClient.post<Bot>(`/bots/${botId}/stop`);
return response.data;
},
onSuccess: (_, botId) => {
queryClient.invalidateQueries({ queryKey: ["bots", botId] });
queryClient.invalidateQueries({ queryKey: ["bots"] });
},
});
}
/**
* Hook to restart a bot.
*/
export function useRestartBot() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (botId: string) => {
const response = await apiClient.post<Bot>(`/bots/${botId}/restart`);
return response.data;
},
onSuccess: (_, botId) => {
queryClient.invalidateQueries({ queryKey: ["bots", botId] });
queryClient.invalidateQueries({ queryKey: ["bots"] });
},
});
}

View File

@@ -0,0 +1,50 @@
/**
* Hooks for fetching statistics data.
*/
import { useQuery } from "@tanstack/react-query";
import apiClient from "@/lib/api/client";
import type { SystemStats, BotStats, AggregateStats } from "@/types/stats";
/**
* Hook to fetch system statistics.
*/
export function useSystemStats() {
return useQuery({
queryKey: ["stats", "system"],
queryFn: async () => {
const response = await apiClient.get<SystemStats>("/stats/system");
return response.data;
},
refetchInterval: 5000, // Refetch every 5 seconds
});
}
/**
* Hook to fetch bot statistics by bot ID.
*/
export function useBotStats(botId: string) {
return useQuery({
queryKey: ["stats", "bot", botId],
queryFn: async () => {
const response = await apiClient.get<BotStats>(`/stats/bots/${botId}`);
return response.data;
},
refetchInterval: 5000,
enabled: !!botId,
});
}
/**
* Hook to fetch aggregate statistics for all bots.
*/
export function useAggregateStats() {
return useQuery({
queryKey: ["stats", "bots", "aggregate"],
queryFn: async () => {
const response = await apiClient.get<AggregateStats>("/stats/bots");
return response.data;
},
refetchInterval: 5000,
});
}