mirror of
https://github.com/azaion/ui.git
synced 2026-06-24 16:31:11 +00:00
Merge API remote base URL config into dev
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+3
-3
@@ -15,10 +15,10 @@
|
||||
# is honored — AZ-498 / contract @
|
||||
# _docs/02_document/contracts/satellite-provider/tiles.md)
|
||||
|
||||
# Prefix for every API request (production: empty; tests / alt deployments: set).
|
||||
# Prefix for every API request (production: empty; remote API / tests: set).
|
||||
# A trailing slash is stripped automatically.
|
||||
# Example: VITE_API_BASE_URL=http://azaion-ui:80
|
||||
VITE_API_BASE_URL=
|
||||
# Example: VITE_API_BASE_URL=https://api.azaion.com
|
||||
VITE_API_BASE_URL=https://api.azaion.com
|
||||
|
||||
# OpenWeatherMap API key. Required for the FlightsPage weather feature.
|
||||
# Leave unset in CI tests — the e2e profile routes to owm-stub.
|
||||
|
||||
+9
-2
@@ -38,6 +38,13 @@ export function getApiBase(): string {
|
||||
return raw.replace(/\/+$/, '')
|
||||
}
|
||||
|
||||
export function authenticatedApiUrl(path: string): string {
|
||||
const url = getApiBase() + path
|
||||
if (!accessToken) return url
|
||||
const separator = url.includes('?') ? '&' : '?'
|
||||
return `${url}${separator}access_token=${encodeURIComponent(accessToken)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Indirection for the failed-refresh redirect. Default impl writes
|
||||
* `'/login'` to `window.location.href` — the production behavior. Tests
|
||||
@@ -68,13 +75,13 @@ async function request<T>(url: string, options: RequestInit = {}): Promise<T> {
|
||||
if (options.body && typeof options.body === 'string') headers.set('Content-Type', 'application/json')
|
||||
|
||||
const fullUrl = getApiBase() + url
|
||||
let res = await fetch(fullUrl, { ...options, headers })
|
||||
let res = await fetch(fullUrl, { ...options, headers, credentials: 'include' })
|
||||
|
||||
if (res.status === 401 && accessToken) {
|
||||
const refreshed = await refreshToken()
|
||||
if (refreshed) {
|
||||
headers.set('Authorization', `Bearer ${accessToken}`)
|
||||
res = await fetch(fullUrl, { ...options, headers })
|
||||
res = await fetch(fullUrl, { ...options, headers, credentials: 'include' })
|
||||
} else {
|
||||
setToken(null)
|
||||
navigateToLoginImpl()
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
export { api, setToken, getToken, getApiBase, setNavigateToLogin } from './client'
|
||||
export { api, setToken, getToken, getApiBase, authenticatedApiUrl, setNavigateToLogin } from './client'
|
||||
export { createSSE } from './sse'
|
||||
export { endpoints } from './endpoints'
|
||||
|
||||
+2
-2
@@ -5,10 +5,10 @@ export function createSSE<T>(
|
||||
onMessage: (data: T) => void,
|
||||
onError?: (err: Event) => void,
|
||||
): () => void {
|
||||
const token = getToken()
|
||||
const prefixedUrl = getApiBase() + url
|
||||
const token = getToken()
|
||||
const fullUrl = token
|
||||
? `${prefixedUrl}${prefixedUrl.includes('?') ? '&' : '?'}access_token=${token}`
|
||||
? `${prefixedUrl}${prefixedUrl.includes('?') ? '&' : '?'}access_token=${encodeURIComponent(token)}`
|
||||
: prefixedUrl
|
||||
|
||||
const source = new EventSource(fullUrl)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { api, endpoints } from '../../api'
|
||||
import { api, endpoints, authenticatedApiUrl } from '../../api'
|
||||
import MediaList from './MediaList'
|
||||
import VideoPlayer, { type VideoPlayerHandle } from './VideoPlayer'
|
||||
import CanvasEditor, { type CanvasEditorHandle } from './CanvasEditor'
|
||||
@@ -195,7 +195,7 @@ export default function AnnotationsPage() {
|
||||
img.crossOrigin = 'anonymous'
|
||||
img.src = selectedMedia.path.startsWith('blob:')
|
||||
? selectedMedia.path
|
||||
: endpoints.annotations.mediaFile(selectedMedia.id)
|
||||
: authenticatedApiUrl(endpoints.annotations.mediaFile(selectedMedia.id))
|
||||
await new Promise(res => { img.onload = res; img.onerror = res })
|
||||
w = img.naturalWidth
|
||||
h = img.naturalHeight
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRef, useEffect, useState, useCallback, forwardRef, useImperativeHandle } from 'react'
|
||||
import { endpoints } from '../../api'
|
||||
import { endpoints, authenticatedApiUrl } from '../../api'
|
||||
import { MediaType } from '../../types'
|
||||
import type { Media, AnnotationListItem, Detection, Affiliation, CombatReadiness } from '../../types'
|
||||
import { getClassColor, getClassNameFallback, hexToRgba } from '../../class-colors'
|
||||
@@ -112,11 +112,11 @@ const CanvasEditor = forwardRef<CanvasEditorHandle, Props>(function CanvasEditor
|
||||
img.crossOrigin = 'anonymous'
|
||||
const isLocalPath = media.path.startsWith('blob:') || media.path.startsWith('data:')
|
||||
if (annotation && !isLocalPath) {
|
||||
img.src = endpoints.annotations.annotationImage(annotation.id)
|
||||
img.src = authenticatedApiUrl(endpoints.annotations.annotationImage(annotation.id))
|
||||
} else if (isLocalPath) {
|
||||
img.src = media.path
|
||||
} else {
|
||||
img.src = endpoints.annotations.mediaFile(media.id)
|
||||
img.src = authenticatedApiUrl(endpoints.annotations.mediaFile(media.id))
|
||||
}
|
||||
img.onload = () => {
|
||||
imgRef.current = img
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRef, useState, useCallback, useEffect, forwardRef, useImperativeHandle } from 'react'
|
||||
import { endpoints } from '../../api'
|
||||
import { endpoints, authenticatedApiUrl } from '../../api'
|
||||
import type { Media } from '../../types'
|
||||
|
||||
interface Props {
|
||||
@@ -49,7 +49,7 @@ const VideoPlayer = forwardRef<VideoPlayerHandle, Props>(function VideoPlayer({
|
||||
|
||||
const videoUrl = media.path.startsWith('blob:')
|
||||
? media.path
|
||||
: endpoints.annotations.mediaFile(media.id)
|
||||
: authenticatedApiUrl(endpoints.annotations.mediaFile(media.id))
|
||||
|
||||
const stepFrames = useCallback((count: number) => {
|
||||
const video = videoRef.current
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { api, endpoints } from '../../api'
|
||||
import { api, endpoints, authenticatedApiUrl } from '../../api'
|
||||
import { useDebounce } from '../../hooks'
|
||||
import { useFlight } from '../../components'
|
||||
import { useSavedAnnotations } from '../../components/SavedAnnotationsContext'
|
||||
@@ -97,7 +97,7 @@ export default function DatasetPage() {
|
||||
imageName: item.imageName,
|
||||
status: item.status,
|
||||
createdDate: item.createdDate,
|
||||
thumbnailUrl: endpoints.annotations.annotationThumbnail(item.annotationId),
|
||||
thumbnailUrl: authenticatedApiUrl(endpoints.annotations.annotationThumbnail(item.annotationId)),
|
||||
isSeed: item.isSeed,
|
||||
isLocal: false,
|
||||
}))
|
||||
|
||||
+2
-1
@@ -13,8 +13,9 @@ export default defineConfig({
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
target: 'https://api.azaion.com',
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user