Skip to content

Commit 554bd0c

Browse files
authored
Remove nginx dependency (#5097)
1 parent 51c6da4 commit 554bd0c

File tree

7 files changed

+69
-81
lines changed

7 files changed

+69
-81
lines changed

.github/workflows/build.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
echo "Testing container with tag: $FIRST_TAG for device: $DEVICE"
116116
117117
# Start the container in detached mode
118-
CONTAINER_ID=$(docker run -d --rm -v .:/app/data -p 80:8080 geti-tune-${DEVICE}:${FIRST_TAG})
118+
CONTAINER_ID=$(docker run -d --rm -v .:/app/data -p 7860:7860 geti-tune-${DEVICE}:${FIRST_TAG})
119119
echo "Started container: $CONTAINER_ID"
120120
121121
# Function to cleanup container on exit
@@ -125,7 +125,7 @@ jobs:
125125
}
126126
trap cleanup EXIT
127127
128-
TEST_URL="geti-tune.localhost"
128+
TEST_URL="localhost:7860"
129129
# Wait for container to be ready (max 60 seconds)
130130
echo "Waiting for container to be ready..."
131131
for i in {1..12}; do

application/backend/app/main.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44
# export no_proxy="localhost, 127.0.0.1, ::1"
55
# Start with:
66
# - uv run app/main.py
7-
# or use docker and access UI and backend at geti-tune.localhost
7+
# or use docker
88
# - docker compose up
99

1010
import importlib
1111
import logging
1212
import pkgutil
13+
from collections.abc import Awaitable, Callable
14+
from os import getenv
1315
from pathlib import Path
16+
from typing import cast
1417

1518
import uvicorn
16-
from fastapi import FastAPI
19+
from fastapi import FastAPI, Request, Response
1720
from fastapi.middleware.cors import CORSMiddleware
1821
from fastapi.responses import FileResponse
22+
from fastapi.staticfiles import StaticFiles
1923
from loguru import logger
2024

2125
from app.api import routers
@@ -39,7 +43,7 @@
3943

4044
app.add_middleware( # TODO restrict settings in production
4145
CORSMiddleware,
42-
allow_origins=["*"],
46+
allow_origins=settings.cors_allowed_origins,
4347
allow_credentials=True,
4448
allow_methods=["*"],
4549
allow_headers=["*"],
@@ -73,6 +77,32 @@ async def health_check() -> dict[str, str]:
7377
return {"status": "ok"}
7478

7579

80+
@app.middleware("http")
81+
async def security_headers_middleware(
82+
request: Request,
83+
call_next: Callable[[Request], Awaitable[Response]],
84+
) -> Response:
85+
"""Add COEP and COOP security headers to all HTTP responses."""
86+
response = await call_next(request)
87+
response.headers.setdefault("Cross-Origin-Embedder-Policy", "require-corp")
88+
response.headers.setdefault("Cross-Origin-Opener-Policy", "same-origin")
89+
return response
90+
91+
92+
static_dir = settings.static_files_dir
93+
if static_dir is not None and static_dir.is_dir() and any(static_dir.iterdir()):
94+
asset_prefix = getenv("ASSET_PREFIX", "/html")
95+
logger.info("Serving static files from {} by context {}", static_dir, asset_prefix)
96+
97+
app.mount(asset_prefix, StaticFiles(directory=static_dir), name="static")
98+
99+
@app.get("/", include_in_schema=False)
100+
@app.get("/{full_path:path}", include_in_schema=False)
101+
async def serve_spa() -> FileResponse:
102+
"""Serve the Single Page Application (SPA) index.html file for any path."""
103+
return FileResponse(cast(Path, static_dir) / "index.html")
104+
105+
76106
def main() -> None:
77107
"""Main application entry point"""
78108
logger.info(f"Starting {settings.app_name} in {settings.environment} mode")

application/backend/app/settings.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@ class Settings(BaseSettings):
3636
# Server
3737
host: str = Field(default="0.0.0.0", alias="HOST") # noqa: S104
3838
port: int = Field(default=7860, alias="PORT")
39+
static_files_dir: Path | None = Field(
40+
default=None,
41+
alias="STATIC_FILES_DIR",
42+
description="Directory containing static UI files",
43+
)
44+
45+
# CORS
46+
cors_origins: str = Field(
47+
default="http://localhost:3000, http://localhost:7860",
48+
alias="CORS_ORIGINS",
49+
)
3950

4051
# Database
4152
database_file: str = Field(default="geti_tune.db", alias="DATABASE_FILE", description="Database filename")
@@ -55,6 +66,11 @@ def database_url(self) -> str:
5566
"""Get database URL"""
5667
return f"sqlite:///{self.data_dir / self.database_file}"
5768

69+
@property
70+
def cors_allowed_origins(self) -> list[str]:
71+
"""Parsed list of allowed CORS origins."""
72+
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
73+
5874
@model_validator(mode="after")
5975
def set_default_dirs(self) -> "Settings":
6076
"""Set default directories based on log_dir"""

application/docker/Dockerfile

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,23 @@ COPY --link ui/tsconfig.json ./
8181
COPY --link ui/rsbuild.config.ts ./
8282
COPY --link ui/src/ src/
8383

84-
ARG PUBLIC_API_BASE_URL=""
85-
ENV PUBLIC_API_BASE_URL=${PUBLIC_API_BASE_URL}
86-
8784
########
8885
# Web UI
8986
########
9087
FROM web-ui-base AS web-ui
88+
89+
ARG ASSET_PREFIX=""
90+
ENV ASSET_PREFIX=${ASSET_PREFIX}
91+
9192
RUN npm run build
9293

9394
#########################
9495
# Base for CPU, GPU and XPU runtime images
9596
#########################
9697
FROM python:3.13-slim@sha256:baf66684c5fcafbda38a54b227ee30ec41e40af1e4073edee3a7110a417756ba AS runtime-base
9798

99+
ARG STATIC_FILES_DIR=""
100+
ENV STATIC_FILES_DIR=${STATIC_FILES_DIR}
98101
ARG UID=10001
99102
ARG USER_NAME="non-root"
100103

@@ -109,34 +112,28 @@ RUN apt-get update \
109112
libgl1=1.7.0-1+b2 \
110113
libglib2.0-0=2.84.4-3~deb13u1 \
111114
libglx-mesa0=25.0.7-2 \
112-
nginx=1.26.* \
113115
&& rm -rf /var/lib/apt/lists/* \
114116
&& apt-get clean
115117

116-
# Nginx config + static UI
117-
COPY docker/nginx.conf /etc/nginx/nginx.conf
118-
COPY --from=web-ui /home/app/web_ui/dist/ /usr/share/nginx/html
119-
120118
# Backend code + uv from backend-base
121119
COPY --link backend/app /application/app
122120
COPY --from=backend-base /bin/uv /bin/uv
121+
COPY --from=web-ui /home/app/web_ui/dist/ ${STATIC_FILES_DIR}
123122

124123
# Make runtime directories writable for non-root user
125-
RUN mkdir -p /home/${USER_NAME}/.cache/uv /tmp/nginx /tmp/nginx/logs && \
124+
RUN mkdir -p /home/${USER_NAME}/.cache/uv && \
126125
chown -R ${UID}:${UID} \
127126
/application \
128-
/usr/share/nginx/html \
129-
/home/${USER_NAME} \
130-
/tmp/nginx
127+
/home/${USER_NAME}
131128

132-
EXPOSE 8080
129+
EXPOSE 7860
133130

134131
WORKDIR /application
135132
ENV PYTHONPATH=/application
136133

137134
USER ${USER_NAME}
138135

139-
CMD ["sh", "-c", "nginx && exec uv run app/main.py;"]
136+
CMD ["uv", "run", "app/main.py"]
140137

141138
######################
142139
# Server (CPU version)

application/docker/docker-compose.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ services:
1515
context: ..
1616
dockerfile: docker/Dockerfile
1717
target: geti-tune-${AI_DEVICE:-cpu}
18+
args:
19+
- ASSET_PREFIX=/html
20+
- STATIC_FILES_DIR=/application/html
1821
<<: *proxies
1922
working_dir: /application
2023
environment:
@@ -23,7 +26,7 @@ services:
2326
- ../backend/data/:/application/data/
2427
- ../backend/logs/:/application/logs/
2528
ports:
26-
- "80:8080"
29+
- "7860:7860"
2730
# Map all host devices to provide access to webcams and other attached devices
2831
privileged: true
2932
devices:

application/docker/nginx.conf

Lines changed: 0 additions & 61 deletions
This file was deleted.

application/ui/rsbuild.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ export default defineConfig({
2929
},
3030
}),
3131
],
32+
output: {
33+
assetPrefix: process.env.ASSET_PREFIX,
34+
},
3235
source: {
3336
define: {
3437
...publicVars,

0 commit comments

Comments
 (0)