cleaup env

This commit is contained in:
2025-09-17 09:52:47 +02:00
parent be3a9002b3
commit 2912b99aa1
10 changed files with 105 additions and 38 deletions

View File

@@ -1,9 +1,19 @@
# =================================== # ===================================
# ENCLAVA MINIMAL CONFIGURATION # ENCLAVA CONFIGURATION
# =================================== # ===================================
# Only essential environment variables that CANNOT have defaults # Only essential environment variables that CANNOT have defaults
# Other settings should be configurable through the app UI # Other settings should be configurable through the app UI
# Admin user (created on first startup only)
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=admin123
# ===================================
# APPLICATION BASE URL (Required - derives all URLs and CORS)
# ===================================
BASE_URL=localhost
# =================================== # ===================================
# INFRASTRUCTURE (Required) # INFRASTRUCTURE (Required)
# =================================== # ===================================
@@ -18,9 +28,7 @@ POSTGRES_PASSWORD=enclava_pass
JWT_SECRET=your-super-secret-jwt-key-here-change-in-production JWT_SECRET=your-super-secret-jwt-key-here-change-in-production
PRIVATEMODE_API_KEY=your-privatemode-api-key-here PRIVATEMODE_API_KEY=your-privatemode-api-key-here
# Admin user (created on first startup only)
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=admin123
# =================================== # ===================================
# ADDITIONAL SECURITY SETTINGS (Optional but recommended) # ADDITIONAL SECURITY SETTINGS (Optional but recommended)
@@ -36,29 +44,31 @@ ADMIN_PASSWORD=admin123
# API Key prefix (default: en_) # API Key prefix (default: en_)
# API_KEY_PREFIX=en_ # API_KEY_PREFIX=en_
# Security thresholds (0.0-1.0)
# API_SECURITY_RISK_THRESHOLD=0.8
# API_SECURITY_WARNING_THRESHOLD=0.6
# API_SECURITY_ANOMALY_THRESHOLD=0.7
# IP security (comma-separated for multiple IPs)
# API_BLOCKED_IPS=
# API_ALLOWED_IPS=
# =================================== # ===================================
# APPLICATION BASE URL (Required - derives all URLs and CORS) # FRONTEND ENVIRONMENT (Required for production)
# =================================== # ===================================
BASE_URL=localhost NODE_ENV=production
# Frontend derives: APP_URL=http://localhost, API_URL=http://localhost, WS_URL=ws://localhost NEXT_PUBLIC_APP_NAME=Enclava
# Backend derives: CORS_ORIGINS=["http://localhost"] # NEXT_PUBLIC_BASE_URL is derived from BASE_URL in Docker configuration
# =================================== # ===================================
# DOCKER NETWORKING (Required for containers) # LOGGING CONFIGURATION
# =================================== # ===================================
BACKEND_INTERNAL_PORT=8000 LOG_LLM_PROMPTS=false
FRONTEND_INTERNAL_PORT=3000
# Hosts are fixed: enclava-backend, enclava-frontend # For production HTTPS deployments, set:
# Upstreams derive: enclava-backend:8000, enclava-frontend:3000 # BASE_URL=your-domain.com
# The system will automatically detect HTTPS and use it for all URLs and CORS
# ===================================
# DOCKER NETWORKING (Optional - defaults provided)
# ===================================
# Internal ports use defaults: backend=8000, frontend=3000
# Override only if you need to change these defaults:
# BACKEND_INTERNAL_PORT=8000
# FRONTEND_INTERNAL_PORT=3000
# =================================== # ===================================
# QDRANT (Required for RAG) # QDRANT (Required for RAG)

View File

@@ -48,7 +48,8 @@ class Settings(BaseSettings):
"""Derive CORS origins from BASE_URL if not explicitly set""" """Derive CORS origins from BASE_URL if not explicitly set"""
if v is None: if v is None:
base_url = info.data.get('BASE_URL', 'localhost') base_url = info.data.get('BASE_URL', 'localhost')
return [f"http://{base_url}"] # Support both HTTP and HTTPS for production environments
return [f"http://{base_url}", f"https://{base_url}"]
return v if isinstance(v, list) else [v] return v if isinstance(v, list) else [v]
# CORS origins (derived from BASE_URL) # CORS origins (derived from BASE_URL)

View File

@@ -23,9 +23,11 @@ services:
# Database migration service - runs once to apply migrations # Database migration service - runs once to apply migrations
enclava-migrate: enclava-migrate:
build: build:
context: ./backend context: ./backend
dockerfile: Dockerfile.prod dockerfile: Dockerfile.prod
env_file:
- ./.env
environment: environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@enclava-postgres:5432/${POSTGRES_DB} - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@enclava-postgres:5432/${POSTGRES_DB}
depends_on: depends_on:
@@ -40,6 +42,8 @@ services:
build: build:
context: ./backend context: ./backend
dockerfile: Dockerfile.prod dockerfile: Dockerfile.prod
env_file:
- ./.env
environment: environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@enclava-postgres:5432/${POSTGRES_DB} - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@enclava-postgres:5432/${POSTGRES_DB}
- REDIS_URL=redis://enclava-redis:6379 - REDIS_URL=redis://enclava-redis:6379
@@ -76,11 +80,11 @@ services:
context: ./frontend context: ./frontend
dockerfile: Dockerfile dockerfile: Dockerfile
target: runner # Use the production stage from multi-stage build target: runner # Use the production stage from multi-stage build
env_file:
- ./.env
environment: environment:
- BASE_URL=${BASE_URL} - BASE_URL=${BASE_URL}
- NEXT_PUBLIC_BASE_URL=${BASE_URL} - NEXT_PUBLIC_BASE_URL=${BASE_URL}
- BACKEND_INTERNAL_PORT=8000
- FRONTEND_INTERNAL_PORT=3000
- INTERNAL_API_URL=http://enclava-backend:8000 - INTERNAL_API_URL=http://enclava-backend:8000
- NODE_ENV=production - NODE_ENV=production
- NEXT_TELEMETRY_DISABLED=1 - NEXT_TELEMETRY_DISABLED=1

View File

@@ -68,11 +68,8 @@ services:
# Required base URL (derives APP/API/WS URLs) # Required base URL (derives APP/API/WS URLs)
- BASE_URL=${BASE_URL} - BASE_URL=${BASE_URL}
- NEXT_PUBLIC_BASE_URL=${BASE_URL} - NEXT_PUBLIC_BASE_URL=${BASE_URL}
# Docker internal ports
- BACKEND_INTERNAL_PORT=${BACKEND_INTERNAL_PORT}
- FRONTEND_INTERNAL_PORT=${FRONTEND_INTERNAL_PORT}
# Internal API URL # Internal API URL
- INTERNAL_API_URL=http://enclava-backend:${BACKEND_INTERNAL_PORT} - INTERNAL_API_URL=http://enclava-backend:8000
depends_on: depends_on:
- enclava-backend - enclava-backend
ports: ports:

View File

@@ -16,8 +16,21 @@ export const viewport: Viewport = {
initialScale: 1, initialScale: 1,
} }
// Function to determine the base URL with proper protocol
const getBaseUrl = () => {
// In production, we need to detect if we're behind HTTPS
if (typeof window !== 'undefined') {
const protocol = window.location.protocol === 'https:' ? 'https' : 'http'
const host = process.env.NEXT_PUBLIC_BASE_URL || window.location.hostname
return `${protocol}://${host}`
}
// For build time/server side, default to HTTP for dev, HTTPS for production
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http'
return `${protocol}://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}`
}
export const metadata: Metadata = { export const metadata: Metadata = {
metadataBase: new URL(`http://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}`), metadataBase: new URL(getBaseUrl()),
title: 'Enclava Platform', title: 'Enclava Platform',
description: 'Secure AI processing platform with plugin-based architecture and confidential computing', description: 'Secure AI processing platform with plugin-based architecture and confidential computing',
keywords: ['AI', 'Enclava', 'Confidential Computing', 'LLM', 'TEE'], keywords: ['AI', 'Enclava', 'Confidential Computing', 'LLM', 'TEE'],
@@ -26,7 +39,7 @@ export const metadata: Metadata = {
openGraph: { openGraph: {
type: 'website', type: 'website',
locale: 'en_US', locale: 'en_US',
url: `http://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}`, url: getBaseUrl(),
title: 'Enclava Platform', title: 'Enclava Platform',
description: 'Secure AI processing platform with plugin-based architecture and confidential computing', description: 'Secure AI processing platform with plugin-based architecture and confidential computing',
siteName: 'Enclava', siteName: 'Enclava',

View File

@@ -91,8 +91,9 @@ export function DocumentUpload({ collections, selectedCollection, onDocumentUplo
updateProgress(60) updateProgress(60)
await uploadFile( await uploadFile(
'/api-internal/v1/rag/documents',
uploadingFile.file, uploadingFile.file,
'/api-internal/v1/rag/documents',
(progress) => updateProgress(progress),
{ collection_id: targetCollection } { collection_id: targetCollection }
) )

View File

@@ -18,6 +18,16 @@ import {
import { User, Settings, Lock, LogOut, ChevronDown } from "lucide-react" import { User, Settings, Lock, LogOut, ChevronDown } from "lucide-react"
import { useState } from "react" import { useState } from "react"
// Helper function to get API URL with proper protocol
const getApiUrl = () => {
if (typeof window !== 'undefined') {
const protocol = window.location.protocol.slice(0, -1) // Remove ':' from 'https:'
const host = window.location.hostname
return `${protocol}://${host}`
}
return `http://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}`
}
export function UserMenu() { export function UserMenu() {
const { user, logout } = useAuth() const { user, logout } = useAuth()
const { toast } = useToast() const { toast } = useToast()
@@ -62,7 +72,7 @@ export function UserMenu() {
throw new Error('Authentication required') throw new Error('Authentication required')
} }
const response = await fetch('/api-internal/v1/auth/change-password', { const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/change-password`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@@ -4,6 +4,16 @@ import { createContext, useContext, useState, useEffect, ReactNode } from "react
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { tokenManager } from "@/lib/token-manager" import { tokenManager } from "@/lib/token-manager"
// Helper function to get API URL with proper protocol
const getApiUrl = () => {
if (typeof window !== 'undefined') {
const protocol = window.location.protocol.slice(0, -1) // Remove ':' from 'https:'
const host = window.location.hostname
return `${protocol}://${host}`
}
return `http://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}`
}
interface User { interface User {
id: string id: string
email: string email: string
@@ -84,7 +94,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const token = await tokenManager.getAccessToken() const token = await tokenManager.getAccessToken()
if (!token) return if (!token) return
const response = await fetch('/api-internal/v1/auth/me', { const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/me`, {
headers: { headers: {
'Authorization': `Bearer ${token}`, 'Authorization': `Bearer ${token}`,
}, },
@@ -114,7 +124,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
setIsLoading(true) setIsLoading(true)
try { try {
const response = await fetch('/api-internal/v1/auth/login', { const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/login`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@@ -1,10 +1,23 @@
import axios from 'axios'; import axios from 'axios';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
const API_BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || ''; // Dynamic base URL with protocol detection
const getApiBaseUrl = (): string => {
if (typeof window !== 'undefined') {
// Client-side: use the same protocol as the current page
const protocol = window.location.protocol.slice(0, -1); // Remove ':' from 'https:'
const host = window.location.hostname;
return `${protocol}://${host}`;
}
// Server-side: use environment variable or default to localhost
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'localhost';
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
return `${protocol}://${baseUrl}`;
};
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: API_BASE_URL, baseURL: getApiBaseUrl(),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
@@ -36,7 +49,7 @@ axiosInstance.interceptors.response.use(
try { try {
const refreshToken = Cookies.get('refresh_token'); const refreshToken = Cookies.get('refresh_token');
if (refreshToken) { if (refreshToken) {
const response = await axios.post(`${API_BASE_URL}/api/auth/refresh`, { const response = await axios.post(`${getApiBaseUrl()}/api/auth/refresh`, {
refresh_token: refreshToken, refresh_token: refreshToken,
}); });

View File

@@ -76,12 +76,20 @@ export const downloadFileFromData = (
export const uploadFile = async ( export const uploadFile = async (
file: File, file: File,
url: string, url: string,
onProgress?: (progress: number) => void onProgress?: (progress: number) => void,
additionalData?: Record<string, any>
): Promise<any> => { ): Promise<any> => {
try { try {
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
// Add additional form data if provided
if (additionalData) {
Object.entries(additionalData).forEach(([key, value]) => {
formData.append(key, value);
});
}
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {