fix: OIDC auth flow, Vite proxy, and PostgreSQL query compat
- Add auth redirect in root layout for unauthenticated users - Proxy OIDC routes (/login, /callback, /logout) through Vite dev server - Strip Secure flag from OIDC cookies in dev mode (HTTP localhost) - Disable retry on auth query to prevent stale cookie loops - Fix SQLite .get()/.all()/.run() calls in category and global-item services for PostgreSQL compatibility - Add userId scoping to category service functions - Add OIDC error logging in auth middleware - Apply linter auto-formatting across affected files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ export function useAuth() {
|
||||
queryKey: ["auth"],
|
||||
queryFn: () => apiGet<AuthState>("/api/auth/me"),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,12 @@ import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as SettingsRouteImport } from './routes/settings'
|
||||
import { Route as LoginRouteImport } from './routes/login'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as GlobalItemsIndexRouteImport } from './routes/global-items/index'
|
||||
import { Route as CollectionIndexRouteImport } from './routes/collection/index'
|
||||
import { Route as UsersUserIdRouteImport } from './routes/users/$userId'
|
||||
import { Route as ThreadsThreadIdRouteImport } from './routes/threads/$threadId'
|
||||
import { Route as SetupsSetupIdRouteImport } from './routes/setups/$setupId'
|
||||
import { Route as GlobalItemsGlobalItemIdRouteImport } from './routes/global-items/$globalItemId'
|
||||
|
||||
const SettingsRoute = SettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
@@ -31,11 +34,21 @@ const IndexRoute = IndexRouteImport.update({
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const GlobalItemsIndexRoute = GlobalItemsIndexRouteImport.update({
|
||||
id: '/global-items/',
|
||||
path: '/global-items/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const CollectionIndexRoute = CollectionIndexRouteImport.update({
|
||||
id: '/collection/',
|
||||
path: '/collection/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const UsersUserIdRoute = UsersUserIdRouteImport.update({
|
||||
id: '/users/$userId',
|
||||
path: '/users/$userId',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const ThreadsThreadIdRoute = ThreadsThreadIdRouteImport.update({
|
||||
id: '/threads/$threadId',
|
||||
path: '/threads/$threadId',
|
||||
@@ -46,31 +59,45 @@ const SetupsSetupIdRoute = SetupsSetupIdRouteImport.update({
|
||||
path: '/setups/$setupId',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const GlobalItemsGlobalItemIdRoute = GlobalItemsGlobalItemIdRouteImport.update({
|
||||
id: '/global-items/$globalItemId',
|
||||
path: '/global-items/$globalItemId',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/settings': typeof SettingsRoute
|
||||
'/global-items/$globalItemId': typeof GlobalItemsGlobalItemIdRoute
|
||||
'/setups/$setupId': typeof SetupsSetupIdRoute
|
||||
'/threads/$threadId': typeof ThreadsThreadIdRoute
|
||||
'/users/$userId': typeof UsersUserIdRoute
|
||||
'/collection/': typeof CollectionIndexRoute
|
||||
'/global-items/': typeof GlobalItemsIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/settings': typeof SettingsRoute
|
||||
'/global-items/$globalItemId': typeof GlobalItemsGlobalItemIdRoute
|
||||
'/setups/$setupId': typeof SetupsSetupIdRoute
|
||||
'/threads/$threadId': typeof ThreadsThreadIdRoute
|
||||
'/users/$userId': typeof UsersUserIdRoute
|
||||
'/collection': typeof CollectionIndexRoute
|
||||
'/global-items': typeof GlobalItemsIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/settings': typeof SettingsRoute
|
||||
'/global-items/$globalItemId': typeof GlobalItemsGlobalItemIdRoute
|
||||
'/setups/$setupId': typeof SetupsSetupIdRoute
|
||||
'/threads/$threadId': typeof ThreadsThreadIdRoute
|
||||
'/users/$userId': typeof UsersUserIdRoute
|
||||
'/collection/': typeof CollectionIndexRoute
|
||||
'/global-items/': typeof GlobalItemsIndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
@@ -78,34 +105,46 @@ export interface FileRouteTypes {
|
||||
| '/'
|
||||
| '/login'
|
||||
| '/settings'
|
||||
| '/global-items/$globalItemId'
|
||||
| '/setups/$setupId'
|
||||
| '/threads/$threadId'
|
||||
| '/users/$userId'
|
||||
| '/collection/'
|
||||
| '/global-items/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/login'
|
||||
| '/settings'
|
||||
| '/global-items/$globalItemId'
|
||||
| '/setups/$setupId'
|
||||
| '/threads/$threadId'
|
||||
| '/users/$userId'
|
||||
| '/collection'
|
||||
| '/global-items'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/login'
|
||||
| '/settings'
|
||||
| '/global-items/$globalItemId'
|
||||
| '/setups/$setupId'
|
||||
| '/threads/$threadId'
|
||||
| '/users/$userId'
|
||||
| '/collection/'
|
||||
| '/global-items/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
LoginRoute: typeof LoginRoute
|
||||
SettingsRoute: typeof SettingsRoute
|
||||
GlobalItemsGlobalItemIdRoute: typeof GlobalItemsGlobalItemIdRoute
|
||||
SetupsSetupIdRoute: typeof SetupsSetupIdRoute
|
||||
ThreadsThreadIdRoute: typeof ThreadsThreadIdRoute
|
||||
UsersUserIdRoute: typeof UsersUserIdRoute
|
||||
CollectionIndexRoute: typeof CollectionIndexRoute
|
||||
GlobalItemsIndexRoute: typeof GlobalItemsIndexRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
@@ -131,6 +170,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/global-items/': {
|
||||
id: '/global-items/'
|
||||
path: '/global-items'
|
||||
fullPath: '/global-items/'
|
||||
preLoaderRoute: typeof GlobalItemsIndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/collection/': {
|
||||
id: '/collection/'
|
||||
path: '/collection'
|
||||
@@ -138,6 +184,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof CollectionIndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/users/$userId': {
|
||||
id: '/users/$userId'
|
||||
path: '/users/$userId'
|
||||
fullPath: '/users/$userId'
|
||||
preLoaderRoute: typeof UsersUserIdRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/threads/$threadId': {
|
||||
id: '/threads/$threadId'
|
||||
path: '/threads/$threadId'
|
||||
@@ -152,6 +205,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof SetupsSetupIdRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/global-items/$globalItemId': {
|
||||
id: '/global-items/$globalItemId'
|
||||
path: '/global-items/$globalItemId'
|
||||
fullPath: '/global-items/$globalItemId'
|
||||
preLoaderRoute: typeof GlobalItemsGlobalItemIdRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,9 +219,12 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
LoginRoute: LoginRoute,
|
||||
SettingsRoute: SettingsRoute,
|
||||
GlobalItemsGlobalItemIdRoute: GlobalItemsGlobalItemIdRoute,
|
||||
SetupsSetupIdRoute: SetupsSetupIdRoute,
|
||||
ThreadsThreadIdRoute: ThreadsThreadIdRoute,
|
||||
UsersUserIdRoute: UsersUserIdRoute,
|
||||
CollectionIndexRoute: CollectionIndexRoute,
|
||||
GlobalItemsIndexRoute: GlobalItemsIndexRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
createRootRoute,
|
||||
type ErrorComponentProps,
|
||||
Outlet,
|
||||
useLocation,
|
||||
useMatchRoute,
|
||||
useNavigate,
|
||||
useRouter,
|
||||
@@ -72,7 +73,8 @@ function RootErrorBoundary({ error, reset }: ErrorComponentProps) {
|
||||
|
||||
function RootLayout() {
|
||||
const navigate = useNavigate();
|
||||
const { data: auth } = useAuth();
|
||||
const location = useLocation();
|
||||
const { data: auth, isLoading: authLoading } = useAuth();
|
||||
const isAuthenticated = !!auth?.user;
|
||||
|
||||
// Item panel state
|
||||
@@ -99,7 +101,7 @@ function RootLayout() {
|
||||
const resolveCandidateId = useUIStore((s) => s.resolveCandidateId);
|
||||
const closeResolveDialog = useUIStore((s) => s.closeResolveDialog);
|
||||
|
||||
// Onboarding
|
||||
// Onboarding — only check when authenticated (endpoint requires auth)
|
||||
const { data: onboardingComplete, isLoading: onboardingLoading } =
|
||||
useOnboardingComplete();
|
||||
const [wizardDismissed, setWizardDismissed] = useState(false);
|
||||
@@ -152,7 +154,30 @@ function RootLayout() {
|
||||
!(collectionSearch as Record<string, string>).tab ||
|
||||
(collectionSearch as Record<string, string>).tab === "gear");
|
||||
|
||||
// Show a minimal loading state while checking onboarding status
|
||||
// Show loading while checking auth
|
||||
if (authLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="w-6 h-6 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect unauthenticated users to login (server-side OIDC route)
|
||||
// Allow public routes through without auth
|
||||
const isPublicRoute =
|
||||
location.pathname.startsWith("/users/") || location.pathname === "/login";
|
||||
|
||||
if (!isAuthenticated && !isPublicRoute) {
|
||||
window.location.href = "/login";
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<p className="text-sm text-gray-500">Redirecting to login...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Show loading while checking onboarding status
|
||||
if (onboardingLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
|
||||
Reference in New Issue
Block a user