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