Update Docker and API configurations for improved job fetching and API proxying

- Adjusted `VITE_API_BASE` in Dockerfile and docker-compose.yml to allow same-origin requests, enhancing compatibility with nginx proxy settings.
- Increased the `top` query limit in `app.py` to 500 to accommodate frontend requirements while ensuring consistency across job fetching in `Jobs.jsx` and `Managers.jsx`.
- Updated nginx configuration to properly proxy API requests, preventing incorrect responses for job-related endpoints.

These changes streamline the interaction between the frontend and backend, ensuring a smoother user experience when fetching job data.
pull/24/head
ahmed.mujtaba 2026-08-20 18:55:49 +05:00
parent 40edc092a3
commit c536def585
6 changed files with 35 additions and 10 deletions

View File

@ -447,7 +447,9 @@ async def fetch_jobs(
department: str | None = Query(None),
requisition_status: str | None = Query(None),
employment_type: str | None = Query(None),
top: int | None = Query(10, ge=1, le=100),
# le=500 (not 100): the Jobs board loads a full client-side page for facets;
# a 200 ceiling used to 422 the SPA and render an empty requisition list.
top: int | None = Query(10, ge=1, le=500),
skip: int = Query(0, ge=0),
# Defaults False, unlike /job/fetch: a requisition list must show CLOSED
# requisitions, and those carry is_active = false. Soft-deleted rows are still

View File

@ -151,10 +151,15 @@ services:
build:
context: ./frontend
args:
# Baked into the bundle at build time — change it and rebuild, not restart.
VITE_API_BASE: ${VITE_API_BASE:-http://localhost:8000}
# Empty = same-origin; nginx proxies API paths to backend-api (see nginx.conf).
# A baked http://localhost:8000 / LAN IP sends the browser to a *different*
# listener than the SPA (Cursor steals 127.0.0.1:8000) and empties /jobs.
VITE_API_BASE: ${VITE_API_BASE:-}
image: hrms-frontend:local
container_name: hrms-frontend
depends_on:
backend-api:
condition: service_healthy
ports:
- "${FRONTEND_PORT:-5173}:80"
healthcheck:

View File

@ -14,11 +14,13 @@ RUN npm ci
COPY . .
# Vite inlines VITE_* at BUILD time, so the API origin is fixed when the image is
# built, not when the container starts — rebuild the image to point it elsewhere.
# `.env.production.local` outranks every other env file, so this wins over the empty
# VITE_API_BASE in .env.production (which means "same origin, behind a proxy").
ARG VITE_API_BASE=http://172.16.204.191:8000
# Empty VITE_API_BASE = same-origin requests. nginx.conf proxies API paths to
# backend-api:8000, so a LAN IP baked into the bundle can no longer send the
# browser to a different listener than the one serving the SPA (the localhost
# vs 127.0.0.1 vs Docker split that emptied /jobs).
# Override with --build-arg VITE_API_BASE=https://api.example.com only when the
# API is intentionally on another origin.
ARG VITE_API_BASE=
RUN printf 'VITE_API_BASE=%s\n' "$VITE_API_BASE" > .env.production.local \
&& npm run build

View File

@ -5,6 +5,20 @@ server {
root /usr/share/nginx/html;
index index.html;
# Same-origin API proxy. The SPA is built with an empty VITE_API_BASE so
# fetch('/jobs/fetch') stays on this host; without this block nginx would
# serve index.html for those paths (200 HTML) and the Jobs board would
# parse an empty payload. backend-api is the compose service name.
location ~ ^/(users|roles|permissions|permission-tags|managers|inbox|email|job|jobs|candidate|notes|interview|feedback|activity|pipeline|notifications|analytics|offers|tasks|assessments|org-settings|saved-searches|search|docs|openapi\.json|redoc)(/|$) {
proxy_pass http://backend-api:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
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;
proxy_set_header Authorization $http_authorization;
}
# One SPA at `/`. `vite dev` and `vite preview` serve index.html for every
# unmatched path; vite.config.js notes that a static deploy needs the equivalent
# rewrite rule. This is it without it /auth/confirm-email (a router path, not a

View File

@ -28,7 +28,7 @@ import * as tasksApi from '../api/tasks'
import { JOB_STATUSES } from '../api/jobs'
import { empTypes, fmtShort } from '../data/seed'
// Must stay within GET /jobs/fetch `top` max (le=100); 200 returns 422 and an empty board.
// Backend allows up to 500; stay at 100 so we match Managers.jsx (shared qk.jobs.list).
const JOB_LIMIT = 100
async function fetchJobs() {

View File

@ -19,7 +19,9 @@ async function fetchManagers() {
}
async function fetchJobs() {
const res = await jobsApi.list({ top: 200 })
// Keep within GET /jobs/fetch `top` ceiling (and match Jobs.jsx) so a shared
// qk.jobs.list() cache entry is never poisoned by a 422 from top=200.
const res = await jobsApi.list({ top: 100 })
const rows = Array.isArray(res?.data) ? res.data : []
return rows.map(jobsApi.toJobView)
}