feat: docker production setup

This commit is contained in:
dextmorgn
2025-10-20 10:42:38 +02:00
parent f7901b710c
commit 58ec5b3d29
7 changed files with 126 additions and 21 deletions

View File

@@ -11,8 +11,7 @@ dev:
prod:
$(MAKE) check-env
docker compose up -d
$(MAKE) frontend_prod
docker compose -f docker-compose.prod.yml up -d --build
check-env:
@echo "🔎 Checking .env files..."
@@ -38,12 +37,11 @@ install:
fi
poetry config virtualenvs.in-project true --local
poetry env use python3.12
docker compose up -d
docker compose up -d postgres redis neo4j
poetry install
cd $(PROJECT_ROOT)/flowsint-core && poetry install
cd $(PROJECT_ROOT)/flowsint-transforms && poetry install
cd $(PROJECT_ROOT)/flowsint-api && poetry install && poetry run alembic upgrade head
cd $(PROJECT_ROOT)/flowsint-app && yarn install
@echo "✅ All modules installed successfully!"
infra:
@@ -53,7 +51,9 @@ api:
cd $(PROJECT_ROOT)/flowsint-api && poetry run uvicorn app.main:app --host 0.0.0.0 --port 5001 --reload
frontend:
cd $(PROJECT_ROOT)/flowsint-app && npm run dev
@echo "🚀 Starting frontend in Docker and opening browser..."
@docker compose up -d flowsint-app
@bash -c 'until curl -s http://localhost:5173 > /dev/null 2>&1; do sleep 1; done; open http://localhost:5173 2>/dev/null || xdg-open http://localhost:5173 2>/dev/null || echo "✅ Frontend ready at http://localhost:5173"'
frontend_prod:
cd $(PROJECT_ROOT)/flowsint-app && npm run build
@@ -61,8 +61,14 @@ frontend_prod:
celery:
cd $(PROJECT_ROOT)/flowsint-core && poetry run celery -A flowsint_core.core.celery worker --loglevel=info --pool=solo
run: infra
$(MAKE) -j3 api frontend celery
run:
@echo "🚀 Starting all services..."
docker compose up -d
@echo "⏳ Waiting for frontend to be ready..."
@bash -c 'until curl -s http://localhost:5173 > /dev/null 2>&1; do sleep 1; done'
@echo "🌐 Opening browser..."
@open http://localhost:5173 2>/dev/null || xdg-open http://localhost:5173 2>/dev/null || echo "✅ All services ready! Frontend at http://localhost:5173"
$(MAKE) -j2 api celery
stop:
@echo "🛑 Stopping all services..."

View File

@@ -67,6 +67,20 @@ services:
networks:
- flowsint_network
# Flowsint frontend app (production)
flowsint-app:
build:
context: ./flowsint-app
dockerfile: Dockerfile
container_name: flowsint-app
ports:
- "5173:80"
depends_on:
- flowsint_api
networks:
- flowsint_network
restart: unless-stopped
networks:
flowsint_network:
name: flowsint_network

View File

@@ -48,6 +48,24 @@ services:
networks:
- flowsint_network
# Flowsint frontend app (development)
flowsint-app:
build:
context: ./flowsint-app
dockerfile: Dockerfile.dev
container_name: flowsint-app
ports:
- "5173:5173"
volumes:
- ./flowsint-app:/app
- /app/node_modules
environment:
- VITE_API_URL=http://localhost:5001
networks:
- flowsint_network
stdin_open: true
tty: true
networks:
flowsint_network:
name: flowsint_network

View File

@@ -19,8 +19,8 @@ FROM nginx:alpine
# Copy built files to nginx
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration if needed
# COPY nginx.conf /etc/nginx/nginx.conf
# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

View File

@@ -0,0 +1,16 @@
FROM node:20
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN yarn install
# Copy source code
COPY . .
# Expose Vite dev server port
EXPOSE 5173
# Run development server with host flag to make it accessible from outside container
CMD ["yarn", "dev", "--host", "0.0.0.0"]

38
flowsint-app/nginx.conf Normal file
View File

@@ -0,0 +1,38 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
# Proxy API requests to backend
location /api {
proxy_pass http://flowsint_api:5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Handle SPA routing - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

View File

@@ -200,12 +200,13 @@ export const useGraphSettingsStore = create<GraphGeneralSettingsStore>()(
// Core methods
updateSetting: (category, key, value) =>
set((state) => {
const categorySettings = state.settings[category as keyof typeof state.settings] as any
const newSettings = {
...state.settings,
[category]: {
...state.settings[category],
...categorySettings,
[key]: {
...state.settings[category][key],
...(categorySettings?.[key] || {}),
value: value
}
}
@@ -217,7 +218,7 @@ export const useGraphSettingsStore = create<GraphGeneralSettingsStore>()(
newForceSettings = {
...state.forceSettings,
[key]: {
...state.forceSettings[key],
...(state.forceSettings[key] || {}),
value: value
}
}
@@ -249,6 +250,7 @@ export const useGraphSettingsStore = create<GraphGeneralSettingsStore>()(
getCategorySettings: (category: string) => {
const categorySettings: Record<string, any> = {}
// @ts-ignore
const settings = get().settings[category]
if (settings) {
Object.entries(settings as Record<string, any>).forEach(([key, setting]) => {
@@ -266,8 +268,8 @@ export const useGraphSettingsStore = create<GraphGeneralSettingsStore>()(
if (!preset) return
set((state) => {
const newSettings = { ...state.settings }
const newForceSettings = { ...state.forceSettings }
const newSettings = { ...state.settings } as any
const newForceSettings = { ...state.forceSettings } as any
Object.entries(preset).forEach(([key, value]) => {
if (newSettings.graph[key]) {
@@ -297,14 +299,25 @@ export const useGraphSettingsStore = create<GraphGeneralSettingsStore>()(
setKeyboardShortcutsOpen: (open) => set({ keyboardShortcutsOpen: open }),
// Helper methods
getSettingValue: (category: string, key: string) => get().settings[category]?.[key]?.value,
getSettingType: (category: string, key: string) => get().settings[category]?.[key]?.type,
getSettingOptions: (category: string, key: string) =>
get().settings[category]?.[key]?.options,
getSettingDescription: (category: string, key: string) =>
get().settings[category]?.[key]?.description,
getSettingValue: (category: string, key: string) => {
const categorySettings = get().settings[category as keyof typeof DEFAULT_SETTINGS] as any
return categorySettings?.[key]?.value
},
getSettingType: (category: string, key: string) => {
const categorySettings = get().settings[category as keyof typeof DEFAULT_SETTINGS] as any
return categorySettings?.[key]?.type
},
getSettingOptions: (category: string, key: string) => {
const categorySettings = get().settings[category as keyof typeof DEFAULT_SETTINGS] as any
return categorySettings?.[key]?.options
},
getSettingDescription: (category: string, key: string) => {
const categorySettings = get().settings[category as keyof typeof DEFAULT_SETTINGS] as any
return categorySettings?.[key]?.description
},
getSettingConstraints: (category: string, key: string) => {
const setting = get().settings[category]?.[key]
const categorySettings = get().settings[category as keyof typeof DEFAULT_SETTINGS] as any
const setting = categorySettings?.[key]
if (setting && 'min' in setting) {
return {
min: setting.min,