split frontend and backend containers
This commit is contained in:
parent
3367a01419
commit
a552406911
9 changed files with 209 additions and 25 deletions
|
|
@ -40,10 +40,10 @@ COPY src/ ./src/
|
|||
RUN echo '#!/bin/bash\n\
|
||||
set -e\n\
|
||||
echo "Starting Python backend..."\n\
|
||||
uv run python src/app.py &\n\
|
||||
uv run python src/main.py &\n\
|
||||
BACKEND_PID=$!\n\
|
||||
echo "Waiting for backend to be ready..."\n\
|
||||
until curl -f http://localhost:8000/search -X POST -H "Content-Type: application/json" -d "{\"query\":\"test\"}" > /dev/null 2>&1; do\n\
|
||||
until curl -f http://localhost:8000/auth/me > /dev/null 2>&1; do\n\
|
||||
echo "Backend not ready yet, waiting..."\n\
|
||||
sleep 2\n\
|
||||
done\n\
|
||||
|
|
|
|||
29
Dockerfile.backend
Normal file
29
Dockerfile.backend
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
FROM python:3.13-slim
|
||||
|
||||
# Install curl for uv installation
|
||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install uv
|
||||
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
ENV PATH="/root/.local/bin:$PATH"
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy Python dependencies
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN uv sync
|
||||
|
||||
# Copy sample document and warmup script for docling
|
||||
COPY documents/2506.08231v1.pdf ./
|
||||
COPY warm_up_docling.py ./
|
||||
RUN uv run python warm_up_docling.py && rm warm_up_docling.py 2506.08231v1.pdf
|
||||
|
||||
# Copy Python source
|
||||
COPY src/ ./src/
|
||||
|
||||
# Expose backend port
|
||||
EXPOSE 8000
|
||||
|
||||
# Start backend in foreground
|
||||
CMD ["uv", "run", "python", "src/main.py"]
|
||||
20
Dockerfile.frontend
Normal file
20
Dockerfile.frontend
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
FROM node:18-slim
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy frontend dependencies
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm install
|
||||
|
||||
# Copy frontend source
|
||||
COPY frontend/ ./
|
||||
|
||||
# Build frontend
|
||||
RUN npm run build
|
||||
|
||||
# Expose frontend port
|
||||
EXPOSE 3000
|
||||
|
||||
# Start frontend in foreground
|
||||
CMD ["npm", "start"]
|
||||
|
|
@ -23,12 +23,11 @@ services:
|
|||
ports:
|
||||
- "5601:5601"
|
||||
|
||||
gendb:
|
||||
image: phact/gendb:latest
|
||||
#build:
|
||||
#context: .
|
||||
#dockerfile: Dockerfile.app
|
||||
container_name: gendb-app
|
||||
gendb-backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.backend
|
||||
container_name: gendb-backend
|
||||
depends_on:
|
||||
- opensearch
|
||||
- langflow
|
||||
|
|
@ -43,17 +42,30 @@ services:
|
|||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
||||
- NVIDIA_VISIBLE_DEVICES=all
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID}
|
||||
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET}
|
||||
volumes:
|
||||
- ./src:/app/src
|
||||
- ./frontend/src:/app/frontend/src
|
||||
- ./pyproject.toml:/app/pyproject.toml
|
||||
- ./uv.lock:/app/uv.lock
|
||||
- ./documents:/app/documents
|
||||
gpus: all
|
||||
platform: linux/amd64
|
||||
|
||||
gendb-frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.frontend
|
||||
container_name: gendb-frontend
|
||||
depends_on:
|
||||
- gendb-backend
|
||||
environment:
|
||||
- GENDB_BACKEND_HOST=gendb-backend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./frontend/src:/app/src
|
||||
|
||||
langflow:
|
||||
volumes:
|
||||
- ./flows:/app/flows
|
||||
|
|
|
|||
|
|
@ -1,14 +1,6 @@
|
|||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: 'http://localhost:8000/:path*',
|
||||
},
|
||||
];
|
||||
},
|
||||
// Increase timeout for API routes
|
||||
experimental: {
|
||||
proxyTimeout: 300000, // 5 minutes
|
||||
|
|
|
|||
92
frontend/src/app/api/[...path]/route.ts
Normal file
92
frontend/src/app/api/[...path]/route.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
return proxyRequest(request, await params);
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
return proxyRequest(request, await params);
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
return proxyRequest(request, await params);
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
return proxyRequest(request, await params);
|
||||
}
|
||||
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
return proxyRequest(request, await params);
|
||||
}
|
||||
|
||||
async function proxyRequest(
|
||||
request: NextRequest,
|
||||
params: { path: string[] }
|
||||
) {
|
||||
const backendHost = process.env.GENDB_BACKEND_HOST || 'localhost';
|
||||
const path = params.path.join('/');
|
||||
const searchParams = request.nextUrl.searchParams.toString();
|
||||
const backendUrl = `http://${backendHost}:8000/${path}${searchParams ? `?${searchParams}` : ''}`;
|
||||
|
||||
try {
|
||||
const body = request.method !== 'GET' && request.method !== 'HEAD'
|
||||
? await request.text()
|
||||
: undefined;
|
||||
|
||||
const headers = new Headers();
|
||||
|
||||
// Copy relevant headers from the original request
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
if (!key.toLowerCase().startsWith('host') &&
|
||||
!key.toLowerCase().startsWith('x-forwarded') &&
|
||||
!key.toLowerCase().startsWith('x-real-ip')) {
|
||||
headers.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(backendUrl, {
|
||||
method: request.method,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
|
||||
const responseBody = await response.text();
|
||||
const responseHeaders = new Headers();
|
||||
|
||||
// Copy response headers
|
||||
for (const [key, value] of response.headers.entries()) {
|
||||
if (!key.toLowerCase().startsWith('transfer-encoding') &&
|
||||
!key.toLowerCase().startsWith('connection')) {
|
||||
responseHeaders.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return new NextResponse(responseBody, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Proxy error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to proxy request' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect, useState, useRef } from "react"
|
||||
import { useEffect, useState, useRef, Suspense } from "react"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Loader2, CheckCircle, XCircle, ArrowLeft } from "lucide-react"
|
||||
import { useAuth } from "@/contexts/auth-context"
|
||||
|
||||
export default function AuthCallbackPage() {
|
||||
function AuthCallbackContent() {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { refreshAuth } = useAuth()
|
||||
|
|
@ -211,4 +211,26 @@ export default function AuthCallbackPage() {
|
|||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function AuthCallbackPage() {
|
||||
return (
|
||||
<Suspense fallback={
|
||||
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="h-5 w-5 animate-spin" />
|
||||
Loading...
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Please wait while we process your request...
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</div>
|
||||
}>
|
||||
<AuthCallbackContent />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useState, useEffect, Suspense } from "react"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
|
|
@ -467,7 +467,9 @@ function ConnectorsPage() {
|
|||
export default function ProtectedConnectorsPage() {
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<ConnectorsPage />
|
||||
<Suspense fallback={<div>Loading connectors...</div>}>
|
||||
<ConnectorsPage />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
import { useEffect, Suspense } from "react"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useAuth } from "@/contexts/auth-context"
|
||||
import { Lock, LogIn, Loader2 } from "lucide-react"
|
||||
|
||||
export default function LoginPage() {
|
||||
function LoginPageContent() {
|
||||
const { isLoading, isAuthenticated, login } = useAuth()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
|
|
@ -62,4 +62,19 @@ export default function LoginPage() {
|
|||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<Suspense fallback={
|
||||
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<Loader2 className="h-8 w-8 animate-spin" />
|
||||
<p className="text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
}>
|
||||
<LoginPageContent />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue