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:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,7 +10,7 @@ dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
backend/lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
||||
@@ -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
|
||||
|
||||
48
frontend/lib/api/client.ts
Normal file
48
frontend/lib/api/client.ts
Normal 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;
|
||||
181
frontend/lib/hooks/useBots.ts
Normal file
181
frontend/lib/hooks/useBots.ts
Normal 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"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
50
frontend/lib/hooks/useStats.ts
Normal file
50
frontend/lib/hooks/useStats.ts
Normal 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,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user