Skip to content

Commit 25262a5

Browse files
authored
Allow running backend on prefixed url (#1495)
1 parent f760d6d commit 25262a5

File tree

17 files changed

+38
-18
lines changed

17 files changed

+38
-18
lines changed

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ ENV NODE_ENV=production
77

88
COPY frontend .
99

10-
RUN corepack enable && CI=true pnpm install && pnpm build
10+
RUN corepack enable && \
11+
CI=true pnpm install && \
12+
VITE_API_BASE_URL=http://localhost:8400/api pnpm build
1113

1214
# Build backend image that also serves frontend (stored in `/app/frontend-dist`)
1315
FROM python:3.14-alpine3.22

backend/bracket/app.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,22 @@ async def generic_exception_handler(request: Request, exc: Exception) -> JSONRes
152152
return JSONResponse({"detail": "Internal server error"}, status_code=500)
153153

154154

155-
app.mount("/static", StaticFiles(directory="static"), name="static")
155+
app.mount(f"{config.api_prefix}/static", StaticFiles(directory="static"), name="static")
156156

157157
for tag, router in routers.items():
158+
assert router.prefix == config.api_prefix, f"Prefix not set on router with tag `{tag}`"
158159
app.include_router(router, tags=[tag])
159160

160161
if config.serve_frontend:
161-
frontend_root = Path("frontend-dist").resolve()
162+
msg = "API_PREFIX env var must be set (e.g. `/api`) when serving the frontend"
163+
assert config.api_prefix.startswith("/"), msg
164+
165+
frontend_root = Path("frontend-dist")
162166
allowed_paths = list(glob.iglob("frontend-dist/**/*", recursive=True))
163167

164168
@app.get("/{full_path:path}")
165169
async def frontend(full_path: str) -> FileResponse:
166-
path = (frontend_root / Path(full_path)).resolve()
170+
path = frontend_root / Path(full_path)
167171

168172
# Checking `str(path) in allowed_paths` should be enough here but we check for more cases
169173
# to be sure and avoid AI tools raising false positives.

backend/bracket/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Config(BaseSettings):
4141
pg_dsn: PostgresDsn = PostgresDsn("postgresql://user:pass@localhost:5432/db")
4242
sentry_dsn: str | None = None
4343
serve_frontend: bool = False
44+
api_prefix: str = ""
4445

4546
def is_cors_enabled(self) -> bool:
4647
return self.cors_origins != "*"

backend/bracket/routes/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from bracket.utils.security import verify_password
2121
from bracket.utils.types import assert_some
2222

23-
router = APIRouter()
23+
router = APIRouter(prefix=config.api_prefix)
2424

2525
ALGORITHM = "HS256"
2626
ACCESS_TOKEN_EXPIRE_MINUTES = 7 * 24 * 60 # 1 week

backend/bracket/routes/clubs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fastapi import APIRouter, Depends
22

3+
from bracket.config import config
34
from bracket.logic.subscriptions import check_requirement
45
from bracket.models.db.club import ClubCreateBody, ClubUpdateBody
56
from bracket.models.db.user import UserPublic
@@ -9,7 +10,7 @@
910
from bracket.utils.errors import ForeignKey, check_foreign_key_violation
1011
from bracket.utils.id_types import ClubId
1112

12-
router = APIRouter()
13+
router = APIRouter(prefix=config.api_prefix)
1314

1415

1516
@router.get("/clubs", response_model=ClubsResponse)

backend/bracket/routes/courts.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from heliclockter import datetime_utc
33
from starlette import status
44

5+
from bracket.config import config
56
from bracket.database import database
67
from bracket.logic.subscriptions import check_requirement
78
from bracket.models.db.court import Court, CourtBody, CourtToInsert
@@ -20,7 +21,7 @@
2021
from bracket.utils.id_types import CourtId, TournamentId
2122
from bracket.utils.types import assert_some
2223

23-
router = APIRouter()
24+
router = APIRouter(prefix=config.api_prefix)
2425

2526

2627
@router.get("/tournaments/{tournament_id}/courts", response_model=CourtsResponse)

backend/bracket/routes/internals.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from fastapi import APIRouter
22
from fastapi.responses import PlainTextResponse
33

4+
from bracket.config import config
45
from bracket.models.metrics import get_request_metrics
56

6-
router = APIRouter()
7+
router = APIRouter(prefix=config.api_prefix)
78

89

910
@router.get("/metrics", response_class=PlainTextResponse)

backend/bracket/routes/matches.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from fastapi import APIRouter, Depends, HTTPException
22
from starlette import status
33

4+
from bracket.config import config
45
from bracket.logic.planning.conflicts import handle_conflicts
56
from bracket.logic.planning.matches import (
67
get_scheduled_matches,
@@ -40,7 +41,7 @@
4041
from bracket.utils.id_types import MatchId, StageItemId, TournamentId
4142
from bracket.utils.types import assert_some
4243

43-
router = APIRouter()
44+
router = APIRouter(prefix=config.api_prefix)
4445

4546

4647
@router.get(

backend/bracket/routes/players.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fastapi import APIRouter, Depends
22

3+
from bracket.config import config
34
from bracket.database import database
45
from bracket.logic.subscriptions import check_requirement
56
from bracket.models.db.player import Player, PlayerBody, PlayerMultiBody
@@ -25,7 +26,7 @@
2526
from bracket.utils.pagination import PaginationPlayers
2627
from bracket.utils.types import assert_some
2728

28-
router = APIRouter()
29+
router = APIRouter(prefix=config.api_prefix)
2930

3031

3132
@router.get("/tournaments/{tournament_id}/players", response_model=PlayersResponse)

backend/bracket/routes/rankings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fastapi import APIRouter, Depends
22

3+
from bracket.config import config
34
from bracket.logic.ranking.calculation import recalculate_ranking_for_stage_item
45
from bracket.logic.ranking.elimination import (
56
update_inputs_in_complete_elimination_stage_item,
@@ -28,7 +29,7 @@
2829
from bracket.sql.stage_items import get_stage_item
2930
from bracket.utils.id_types import RankingId, TournamentId
3031

31-
router = APIRouter()
32+
router = APIRouter(prefix=config.api_prefix)
3233

3334

3435
@router.get("/tournaments/{tournament_id}/rankings")

0 commit comments

Comments
 (0)