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\
|
RUN echo '#!/bin/bash\n\
|
||||||
set -e\n\
|
set -e\n\
|
||||||
echo "Starting Python backend..."\n\
|
echo "Starting Python backend..."\n\
|
||||||
uv run python src/app.py &\n\
|
uv run python src/main.py &\n\
|
||||||
BACKEND_PID=$!\n\
|
BACKEND_PID=$!\n\
|
||||||
echo "Waiting for backend to be ready..."\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\
|
echo "Backend not ready yet, waiting..."\n\
|
||||||
sleep 2\n\
|
sleep 2\n\
|
||||||
done\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:
|
ports:
|
||||||
- "5601:5601"
|
- "5601:5601"
|
||||||
|
|
||||||
gendb:
|
gendb-backend:
|
||||||
image: phact/gendb:latest
|
build:
|
||||||
#build:
|
context: .
|
||||||
#context: .
|
dockerfile: Dockerfile.backend
|
||||||
#dockerfile: Dockerfile.app
|
container_name: gendb-backend
|
||||||
container_name: gendb-app
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- opensearch
|
- opensearch
|
||||||
- langflow
|
- langflow
|
||||||
|
|
@ -43,17 +42,30 @@ services:
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
||||||
- NVIDIA_VISIBLE_DEVICES=all
|
- NVIDIA_VISIBLE_DEVICES=all
|
||||||
ports:
|
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID}
|
||||||
- "3000:3000"
|
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./src:/app/src
|
- ./src:/app/src
|
||||||
- ./frontend/src:/app/frontend/src
|
|
||||||
- ./pyproject.toml:/app/pyproject.toml
|
- ./pyproject.toml:/app/pyproject.toml
|
||||||
- ./uv.lock:/app/uv.lock
|
- ./uv.lock:/app/uv.lock
|
||||||
- ./documents:/app/documents
|
- ./documents:/app/documents
|
||||||
gpus: all
|
gpus: all
|
||||||
platform: linux/amd64
|
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:
|
langflow:
|
||||||
volumes:
|
volumes:
|
||||||
- ./flows:/app/flows
|
- ./flows:/app/flows
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,6 @@
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
async rewrites() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: '/api/:path*',
|
|
||||||
destination: 'http://localhost:8000/:path*',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
// Increase timeout for API routes
|
// Increase timeout for API routes
|
||||||
experimental: {
|
experimental: {
|
||||||
proxyTimeout: 300000, // 5 minutes
|
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"
|
"use client"
|
||||||
|
|
||||||
import { useEffect, useState, useRef } from "react"
|
import { useEffect, useState, useRef, Suspense } from "react"
|
||||||
import { useRouter, useSearchParams } from "next/navigation"
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Loader2, CheckCircle, XCircle, ArrowLeft } from "lucide-react"
|
import { Loader2, CheckCircle, XCircle, ArrowLeft } from "lucide-react"
|
||||||
import { useAuth } from "@/contexts/auth-context"
|
import { useAuth } from "@/contexts/auth-context"
|
||||||
|
|
||||||
export default function AuthCallbackPage() {
|
function AuthCallbackContent() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const { refreshAuth } = useAuth()
|
const { refreshAuth } = useAuth()
|
||||||
|
|
@ -212,3 +212,25 @@ export default function AuthCallbackPage() {
|
||||||
</div>
|
</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"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect, Suspense } from "react"
|
||||||
import { useSearchParams } from "next/navigation"
|
import { useSearchParams } from "next/navigation"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
|
|
@ -467,7 +467,9 @@ function ConnectorsPage() {
|
||||||
export default function ProtectedConnectorsPage() {
|
export default function ProtectedConnectorsPage() {
|
||||||
return (
|
return (
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
<ConnectorsPage />
|
<Suspense fallback={<div>Loading connectors...</div>}>
|
||||||
|
<ConnectorsPage />
|
||||||
|
</Suspense>
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect } from "react"
|
import { useEffect, Suspense } from "react"
|
||||||
import { useRouter, useSearchParams } from "next/navigation"
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { useAuth } from "@/contexts/auth-context"
|
import { useAuth } from "@/contexts/auth-context"
|
||||||
import { Lock, LogIn, Loader2 } from "lucide-react"
|
import { Lock, LogIn, Loader2 } from "lucide-react"
|
||||||
|
|
||||||
export default function LoginPage() {
|
function LoginPageContent() {
|
||||||
const { isLoading, isAuthenticated, login } = useAuth()
|
const { isLoading, isAuthenticated, login } = useAuth()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
|
@ -63,3 +63,18 @@ export default function LoginPage() {
|
||||||
</div>
|
</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