fix: support HTTPS proxy by using relative API paths
All checks were successful
CI / skip-ci-check (pull_request) Successful in 1m51s
CI / lint-and-type-check (pull_request) Successful in 2m32s
CI / python-lint (pull_request) Successful in 2m16s
CI / test-backend (pull_request) Successful in 4m11s
CI / build (pull_request) Successful in 4m59s
CI / secret-scanning (pull_request) Successful in 1m59s
CI / dependency-scan (pull_request) Successful in 1m58s
CI / sast-scan (pull_request) Successful in 3m7s
CI / workflow-summary (pull_request) Successful in 1m50s

- Update API client to use relative paths when VITE_API_URL is empty
- Fix EventSource URLs to use window.location.origin for proxy compatibility
- Update image/video URL construction to use relative paths
- Add debug logging for API response troubleshooting
- All API calls now work correctly when served through HTTPS proxy

This fixes mixed content errors and allows the admin frontend to work
when accessed via HTTPS domain with reverse proxy (Caddy/nginx).
This commit is contained in:
tanyar09 2026-01-23 18:59:46 +00:00
parent 49ae9728f3
commit 70cfd63ca1
8 changed files with 52 additions and 12 deletions

View File

@ -48,8 +48,12 @@ export const authApi = {
},
me: async (): Promise<UserResponse> => {
const { data } = await apiClient.get<UserResponse>('/api/v1/auth/me')
return data
const response = await apiClient.get<UserResponse>('/api/v1/auth/me')
console.log('🔍 Raw /me API response:', response)
console.log('🔍 Response data:', response.data)
console.log('🔍 Response data type:', typeof response.data)
console.log('🔍 Response data keys:', response.data ? Object.keys(response.data) : 'no keys')
return response.data
},
changePassword: async (

View File

@ -3,7 +3,11 @@ import axios from 'axios'
// Get API base URL from environment variable or use default
// The .env file should contain: VITE_API_URL=http://127.0.0.1:8000
// Alternatively, Vite proxy can be used (configured in vite.config.ts) by setting VITE_API_URL to empty string
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000'
// When VITE_API_URL is empty/undefined, use relative path to work with HTTPS proxy
const envApiUrl = import.meta.env.VITE_API_URL
const API_BASE_URL = envApiUrl && envApiUrl.trim() !== ''
? envApiUrl
: '' // Use relative path when empty - works with proxy and HTTPS
export const apiClient = axios.create({
baseURL: API_BASE_URL,

View File

@ -27,8 +27,11 @@ export const jobsApi = {
},
streamJobProgress: (jobId: string): EventSource => {
// EventSource needs absolute URL - use VITE_API_URL or fallback to direct backend URL
const baseURL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000'
// EventSource needs absolute URL - use VITE_API_URL or construct from current origin
const envApiUrl = import.meta.env.VITE_API_URL
const baseURL = envApiUrl && envApiUrl.trim() !== ''
? envApiUrl
: window.location.origin // Use current origin when empty - works with proxy and HTTPS
return new EventSource(`${baseURL}/api/v1/jobs/stream/${jobId}`)
},

View File

@ -70,8 +70,11 @@ export const photosApi = {
},
streamJobProgress: (jobId: string): EventSource => {
// EventSource needs absolute URL - use VITE_API_URL or fallback to direct backend URL
const baseURL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000'
// EventSource needs absolute URL - use VITE_API_URL or construct from current origin
const envApiUrl = import.meta.env.VITE_API_URL
const baseURL = envApiUrl && envApiUrl.trim() !== ''
? envApiUrl
: window.location.origin // Use current origin when empty - works with proxy and HTTPS
return new EventSource(`${baseURL}/api/v1/jobs/stream/${jobId}`)
},

View File

@ -108,12 +108,18 @@ export const videosApi = {
},
getThumbnailUrl: (videoId: number): string => {
const baseURL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000'
const envApiUrl = import.meta.env.VITE_API_URL
const baseURL = envApiUrl && envApiUrl.trim() !== ''
? envApiUrl
: '' // Use relative path when empty - works with proxy and HTTPS
return `${baseURL}/api/v1/videos/${videoId}/thumbnail`
},
getVideoUrl: (videoId: number): string => {
const baseURL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000'
const envApiUrl = import.meta.env.VITE_API_URL
const baseURL = envApiUrl && envApiUrl.trim() !== ''
? envApiUrl
: '' // Use relative path when empty - works with proxy and HTTPS
return `${baseURL}/api/v1/videos/${videoId}/video`
},
}

View File

@ -38,6 +38,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
authApi
.me()
.then((user) => {
console.log('🔍 Auth /me response:', {
username: user.username,
is_admin: user.is_admin,
role: user.role,
permissions: user.permissions
})
setAuthState({
isAuthenticated: true,
username: user.username,
@ -81,6 +87,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
localStorage.setItem('access_token', tokens.access_token)
localStorage.setItem('refresh_token', tokens.refresh_token)
const user = await authApi.me()
console.log('🔍 Login /me response:', {
username: user.username,
is_admin: user.is_admin,
role: user.role,
permissions: user.permissions
})
const passwordChangeRequired = tokens.password_change_required || false
setAuthState({
isAuthenticated: true,
@ -133,7 +145,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
if (authState.isAdmin) {
return true
}
return Boolean(authState.permissions[featureKey])
const hasPerm = Boolean(authState.permissions[featureKey])
console.log(`🔍 hasPermission(${featureKey}):`, {
isAdmin: authState.isAdmin,
hasPerm,
permissions: authState.permissions
})
return hasPerm
},
[authState.isAdmin, authState.permissions]
)

View File

@ -604,7 +604,8 @@ export default function Identify() {
const preloadImages = () => {
const preloadUrls: string[] = []
const baseUrl = apiClient.defaults.baseURL || 'http://127.0.0.1:8000'
// Use relative path when baseURL is empty (works with proxy and HTTPS)
const baseUrl = apiClient.defaults.baseURL || ''
// Preload next face
if (currentIdx + 1 < faces.length) {

View File

@ -41,7 +41,8 @@ export default function ReportedPhotos() {
// Create direct backend URLs for images (only for non-video photos)
const newImageUrls: Record<number, string> = {}
const baseURL = apiClient.defaults.baseURL || 'http://10.0.10.121:8000'
// Use relative path when baseURL is empty (works with proxy and HTTPS)
const baseURL = apiClient.defaults.baseURL || ''
response.items.forEach((reported) => {
if (reported.photo_id && reported.photo_media_type !== 'video') {
// Use direct backend URL - the backend endpoint doesn't require auth for images