Add chat page and chat server
This commit is contained in:
@@ -19,6 +19,7 @@ import { AuthProvider } from './contexts/AuthContext';
|
||||
import { Protected } from './components/auth/Protected';
|
||||
import { FirstAccessiblePage } from './components/auth/FirstAccessiblePage';
|
||||
import { Brands } from '@/pages/Brands';
|
||||
import { Chat } from '@/pages/Chat';
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
function App() {
|
||||
@@ -133,6 +134,11 @@ function App() {
|
||||
<Forecasting />
|
||||
</Protected>
|
||||
} />
|
||||
<Route path="/chat" element={
|
||||
<Protected page="chat">
|
||||
<Chat />
|
||||
</Protected>
|
||||
} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
177
inventory/src/components/chat/ChatTest.tsx
Normal file
177
inventory/src/components/chat/ChatTest.tsx
Normal file
@@ -0,0 +1,177 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Loader2, Hash, Lock, Users, MessageSquare } from 'lucide-react';
|
||||
import config from '@/config';
|
||||
|
||||
interface Room {
|
||||
id: number;
|
||||
name: string;
|
||||
fname: string;
|
||||
type: string;
|
||||
msgs: number;
|
||||
last_message_date: string;
|
||||
}
|
||||
|
||||
interface ChatTestProps {
|
||||
selectedUserId: string;
|
||||
}
|
||||
|
||||
export function ChatTest({ selectedUserId }: ChatTestProps) {
|
||||
const [rooms, setRooms] = useState<Room[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedUserId) {
|
||||
setRooms([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchUserRooms = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${config.chatUrl}/users/${selectedUserId}/rooms`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status === 'success') {
|
||||
setRooms(data.rooms);
|
||||
} else {
|
||||
throw new Error(data.error || 'Failed to fetch rooms');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching user rooms:', err);
|
||||
setError(err instanceof Error ? err.message : 'An error occurred');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserRooms();
|
||||
}, [selectedUserId]);
|
||||
|
||||
const getRoomIcon = (roomType: string) => {
|
||||
switch (roomType) {
|
||||
case 'c':
|
||||
return <Hash className="h-4 w-4 text-blue-500" />;
|
||||
case 'p':
|
||||
return <Lock className="h-4 w-4 text-orange-500" />;
|
||||
case 'd':
|
||||
return <MessageSquare className="h-4 w-4 text-green-500" />;
|
||||
default:
|
||||
return <Users className="h-4 w-4 text-gray-500" />;
|
||||
}
|
||||
};
|
||||
|
||||
const getRoomTypeLabel = (roomType: string) => {
|
||||
switch (roomType) {
|
||||
case 'c':
|
||||
return 'Channel';
|
||||
case 'p':
|
||||
return 'Private';
|
||||
case 'd':
|
||||
return 'Direct';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
};
|
||||
|
||||
if (!selectedUserId) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Database Connection Test</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground">
|
||||
Select a user from the dropdown above to view their rooms and test the database connection.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Loading User Rooms...</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<span>Fetching rooms for selected user...</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Card className="border-red-200 bg-red-50">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-red-800">Error Loading Rooms</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-red-700">{error}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>User Rooms ({rooms.length})</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Rooms accessible to the selected user
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{rooms.length === 0 ? (
|
||||
<p className="text-muted-foreground">No rooms found for this user.</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{rooms.map((room) => (
|
||||
<div
|
||||
key={room.id}
|
||||
className="flex items-center justify-between p-3 border rounded-lg hover:bg-gray-50"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
{getRoomIcon(room.type)}
|
||||
<div>
|
||||
<div className="font-medium">
|
||||
{room.fname || room.name || 'Unnamed Room'}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{room.name && room.fname !== room.name && (
|
||||
<span className="font-mono">#{room.name}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="secondary">
|
||||
{getRoomTypeLabel(room.type)}
|
||||
</Badge>
|
||||
<Badge variant="outline">
|
||||
{room.msgs} messages
|
||||
</Badge>
|
||||
{room.last_message_date && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{new Date(room.last_message_date).toLocaleDateString()}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Plus,
|
||||
ShoppingBag,
|
||||
Truck,
|
||||
MessageCircle,
|
||||
} from "lucide-react";
|
||||
import { IconCrystalBall } from "@tabler/icons-react";
|
||||
import {
|
||||
@@ -81,6 +82,12 @@ const items = [
|
||||
icon: Plus,
|
||||
url: "/import",
|
||||
permission: "access:import"
|
||||
},
|
||||
{
|
||||
title: "Chat",
|
||||
icon: MessageCircle,
|
||||
url: "/chat",
|
||||
permission: "access:chat"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ const isDev = import.meta.env.DEV;
|
||||
const config = {
|
||||
apiUrl: isDev ? '/api' : 'https://inventory.kent.pw/api',
|
||||
baseUrl: isDev ? '' : 'https://inventory.kent.pw',
|
||||
authUrl: isDev ? '/auth-inv' : 'https://inventory.kent.pw/auth-inv'
|
||||
authUrl: isDev ? '/auth-inv' : 'https://inventory.kent.pw/auth-inv',
|
||||
chatUrl: isDev ? '/chat-api' : 'https://inventory.kent.pw/chat-api'
|
||||
};
|
||||
|
||||
export default config;
|
||||
167
inventory/src/pages/Chat.tsx
Normal file
167
inventory/src/pages/Chat.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Loader2, MessageCircle, Users, Database } from 'lucide-react';
|
||||
import { ChatTest } from '@/components/chat/ChatTest';
|
||||
import config from '@/config';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
type: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
interface DbStats {
|
||||
active_users: number;
|
||||
total_messages: number;
|
||||
total_rooms: number;
|
||||
}
|
||||
|
||||
export function Chat() {
|
||||
const [users, setUsers] = useState<User[]>([]);
|
||||
const [selectedUserId, setSelectedUserId] = useState<string>('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [dbStats, setDbStats] = useState<DbStats | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchInitialData = async () => {
|
||||
try {
|
||||
// Test database connection
|
||||
const dbResponse = await fetch(`${config.chatUrl}/test-db`);
|
||||
const dbData = await dbResponse.json();
|
||||
|
||||
if (dbData.status === 'success') {
|
||||
setDbStats(dbData.stats);
|
||||
} else {
|
||||
throw new Error(dbData.error || 'Database connection failed');
|
||||
}
|
||||
|
||||
// Fetch users
|
||||
const usersResponse = await fetch(`${config.chatUrl}/users`);
|
||||
const usersData = await usersResponse.json();
|
||||
|
||||
if (usersData.status === 'success') {
|
||||
setUsers(usersData.users);
|
||||
} else {
|
||||
throw new Error(usersData.error || 'Failed to fetch users');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching initial data:', err);
|
||||
setError(err instanceof Error ? err.message : 'An error occurred');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchInitialData();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<Loader2 className="h-8 w-8 animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<Card className="border-red-200 bg-red-50">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-red-800">Connection Error</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-red-700">{error}</p>
|
||||
<p className="text-sm text-red-600 mt-2">
|
||||
Make sure the chat server is running on port 3014 and the database is accessible.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">Chat Archive</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Read-only archive of Rocket.Chat conversations
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={selectedUserId} onValueChange={setSelectedUserId}>
|
||||
<SelectTrigger className="w-64">
|
||||
<SelectValue placeholder="View as user..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{users.map((user) => (
|
||||
<SelectItem key={user.id} value={user.id.toString()}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>{user.name || user.username}</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{user.username}
|
||||
</Badge>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Database Stats */}
|
||||
{dbStats && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="h-5 w-5 text-blue-500" />
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Active Users</p>
|
||||
<p className="text-2xl font-bold">{dbStats.active_users}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<MessageCircle className="h-5 w-5 text-green-500" />
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Total Messages</p>
|
||||
<p className="text-2xl font-bold">{dbStats.total_messages.toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="h-5 w-5 text-purple-500" />
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Total Rooms</p>
|
||||
<p className="text-2xl font-bold">{dbStats.total_rooms}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Chat Test Component */}
|
||||
<ChatTest selectedUserId={selectedUserId} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
import path from "path";
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { loadEnv } from "vite";
|
||||
import fs from 'fs-extra';
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(function (_a) {
|
||||
var mode = _a.mode;
|
||||
var env = loadEnv(mode, process.cwd(), "");
|
||||
var isDev = mode === 'development';
|
||||
return {
|
||||
plugins: [
|
||||
react(),
|
||||
{
|
||||
name: 'copy-build',
|
||||
closeBundle: function () { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var sourcePath, targetPath, error_1;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!!isDev) return [3 /*break*/, 6];
|
||||
sourcePath = path.resolve(__dirname, 'build');
|
||||
targetPath = path.resolve(__dirname, '../inventory-server/frontend/build');
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, 5, , 6]);
|
||||
return [4 /*yield*/, fs.ensureDir(path.dirname(targetPath))];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, fs.remove(targetPath)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, fs.copy(sourcePath, targetPath)];
|
||||
case 4:
|
||||
_a.sent();
|
||||
console.log('Build files copied successfully to server directory!');
|
||||
return [3 /*break*/, 6];
|
||||
case 5:
|
||||
error_1 = _a.sent();
|
||||
console.error('Error copying build files:', error_1);
|
||||
process.exit(1);
|
||||
return [3 /*break*/, 6];
|
||||
case 6: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); }
|
||||
}
|
||||
],
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(mode)
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 5175,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "https://inventory.kent.pw",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
xfwd: true,
|
||||
cookieDomainRewrite: "",
|
||||
withCredentials: true,
|
||||
rewrite: function (path) { return path.replace(/^\/api/, "/api"); },
|
||||
configure: function (proxy, _options) {
|
||||
proxy.on("error", function (err, req, res) {
|
||||
console.log("API proxy error:", err);
|
||||
res.writeHead(500, {
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
res.end(JSON.stringify({ error: "Proxy Error", message: err.message }));
|
||||
});
|
||||
proxy.on("proxyReq", function (proxyReq, req, _res) {
|
||||
console.log("Outgoing request to API:", {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
headers: proxyReq.getHeaders(),
|
||||
});
|
||||
});
|
||||
proxy.on("proxyRes", function (proxyRes, req, _res) {
|
||||
console.log("API Proxy response:", {
|
||||
statusCode: proxyRes.statusCode,
|
||||
url: req.url,
|
||||
headers: proxyRes.headers,
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
"/auth-inv": {
|
||||
target: "https://inventory.kent.pw",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
xfwd: true,
|
||||
cookieDomainRewrite: {
|
||||
"inventory.kent.pw": "localhost"
|
||||
},
|
||||
withCredentials: true,
|
||||
onProxyReq: function (proxyReq, req) {
|
||||
// Add origin header to match CORS policy
|
||||
proxyReq.setHeader('Origin', 'http://localhost:5175');
|
||||
},
|
||||
rewrite: function (path) { return path.replace(/^\/auth-inv/, "/auth-inv"); },
|
||||
configure: function (proxy, _options) {
|
||||
proxy.on("error", function (err, req, res) {
|
||||
console.log("Auth proxy error:", err);
|
||||
res.writeHead(500, {
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
res.end(JSON.stringify({ error: "Proxy Error", message: err.message }));
|
||||
});
|
||||
proxy.on("proxyReq", function (proxyReq, req, _res) {
|
||||
console.log("Outgoing request to Auth:", {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
headers: proxyReq.getHeaders(),
|
||||
});
|
||||
});
|
||||
proxy.on("proxyRes", function (proxyRes, req, _res) {
|
||||
console.log("Auth Proxy response:", {
|
||||
statusCode: proxyRes.statusCode,
|
||||
url: req.url,
|
||||
headers: proxyRes.headers,
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
"/uploads": {
|
||||
target: "https://inventory.kent.pw",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: function (path) { return path; },
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: "build",
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
vendor: ["react", "react-dom", "react-router-dom"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -120,6 +120,41 @@ export default defineConfig(({ mode }) => {
|
||||
})
|
||||
},
|
||||
},
|
||||
"/chat-api": {
|
||||
target: "https://inventory.kent.pw",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
xfwd: true,
|
||||
cookieDomainRewrite: "",
|
||||
withCredentials: true,
|
||||
rewrite: (path) => path,
|
||||
configure: (proxy, _options) => {
|
||||
proxy.on("error", (err, req, res) => {
|
||||
console.log("Chat API proxy error:", err)
|
||||
res.writeHead(500, {
|
||||
"Content-Type": "application/json",
|
||||
})
|
||||
res.end(
|
||||
JSON.stringify({ error: "Proxy Error", message: err.message })
|
||||
)
|
||||
})
|
||||
proxy.on("proxyReq", (proxyReq, req, _res) => {
|
||||
console.log("Outgoing request to Chat API:", {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
headers: proxyReq.getHeaders(),
|
||||
})
|
||||
})
|
||||
proxy.on("proxyRes", (proxyRes, req, _res) => {
|
||||
console.log("Chat API Proxy response:", {
|
||||
statusCode: proxyRes.statusCode,
|
||||
url: req.url,
|
||||
headers: proxyRes.headers,
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
"/uploads": {
|
||||
target: "https://inventory.kent.pw",
|
||||
changeOrigin: true,
|
||||
|
||||
Reference in New Issue
Block a user