feat: runtime feature flags, upload/download, RAW decoding
Adds Redis-backed feature flags for vision stages with admin UI toggles and manual backfill trigger, photo upload and download routers with frontend upload modal, and rawpy-based RAW decoding with JPEG fallback for misnamed DNGs. Fixes pgvector serialization, is_trashed filter, and naive-datetime bind in incremental duplicate regrouping; bumps Celery time limits on regroup tasks beyond the 5-minute default. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
39
frontend/src/hooks/useFeaturesQuery.ts
Normal file
39
frontend/src/hooks/useFeaturesQuery.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { features, type FeaturesMap } from '../services/api'
|
||||
|
||||
export const FEATURES_QUERY_KEY = ['features'] as const
|
||||
|
||||
/** Read the effective feature-flag state (admin override or YAML
|
||||
* default). Powers conditional rendering of pipeline-dependent UI —
|
||||
* People view, Tags view, OCR snippets, etc. */
|
||||
export function useFeaturesQuery() {
|
||||
return useQuery<FeaturesMap>({
|
||||
queryKey: FEATURES_QUERY_KEY,
|
||||
queryFn: features.list,
|
||||
// Re-read every minute so admin toggles reflect without a page
|
||||
// reload. The admin tab also invalidates this key on write so the
|
||||
// refresh can be immediate for the admin who just flipped it.
|
||||
staleTime: 60_000,
|
||||
refetchInterval: 60_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useIsFeatureEnabled(
|
||||
name:
|
||||
| 'vision.enabled'
|
||||
| 'vision.ocr.enabled'
|
||||
| 'vision.detector.enabled'
|
||||
| 'vision.faces.enabled'
|
||||
| 'vision.classifier.enabled',
|
||||
): boolean {
|
||||
const { data } = useFeaturesQuery()
|
||||
// Default to enabled while loading so we don't flash "feature off"
|
||||
// during a first-paint fetch. The backend is the source of truth;
|
||||
// any gated UI that slipped through just returns empty data anyway.
|
||||
if (!data) return true
|
||||
return !!data[name]
|
||||
}
|
||||
|
||||
export function invalidateFeaturesQuery(queryClient: ReturnType<typeof useQueryClient>) {
|
||||
queryClient.invalidateQueries({ queryKey: FEATURES_QUERY_KEY })
|
||||
}
|
||||
Reference in New Issue
Block a user