Files
k-photos/k-photos-frontend/lib/api.ts
Gabriel Kaszewski 957737ac9b feat: frontend MVP — auth, timeline, upload, albums, admin, image viewer
Backend:
- user roles (DB + JWT + first-user-is-admin)
- volume-aware file resolver (multi-volume asset serving)
- directory scanner uses volume URI directly
- date-summary endpoint (capture date from EXIF)
- timeline ordered by capture date
- list endpoints: volumes, plugins, pipelines, library paths
- delete endpoints: volumes, library paths
- configurable upload body limit (MAX_UPLOAD_BYTES)

Frontend:
- auth: login/register, token refresh, role-based admin gate
- timeline: date-grouped grid, infinite scroll, date scrubber
- image viewer: fullscreen zoom/pan/pinch, metadata sidebar
- upload: drag-drop, sequential upload, progress tracking
- albums: create, add/remove photos, asset picker dialog
- admin: storage (import library), jobs (pagination, error details),
  plugins (list + toggle), pipelines, sidecars, duplicates
- multi-select mode with add-to-album action
- TanStack Query for all data fetching
2026-06-01 01:35:43 +02:00

63 lines
1.4 KiB
TypeScript

import axios from "axios"
import { getTokens, setTokens, clearTokens } from "./auth"
import type { AuthResponse } from "./types"
const api = axios.create({
baseURL: "/api/v1",
})
api.interceptors.request.use((config) => {
const { access } = getTokens()
if (access) {
config.headers.Authorization = `Bearer ${access}`
}
return config
})
let refreshPromise: Promise<string> | null = null
api.interceptors.response.use(
(res) => res,
async (error) => {
const original = error.config
if (error.response?.status !== 401 || original._retry) {
return Promise.reject(error)
}
original._retry = true
const { refresh } = getTokens()
if (!refresh) {
clearTokens()
window.location.href = "/login"
return Promise.reject(error)
}
if (!refreshPromise) {
refreshPromise = axios
.post<AuthResponse>("/api/v1/auth/refresh", {
refresh_token: refresh,
})
.then((res) => {
setTokens(res.data.token, res.data.refresh_token)
return res.data.token
})
.catch(() => {
clearTokens()
window.location.href = "/login"
return ""
})
.finally(() => {
refreshPromise = null
})
}
const newToken = await refreshPromise
if (!newToken) return Promise.reject(error)
original.headers.Authorization = `Bearer ${newToken}`
return api(original)
},
)
export default api