diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx index 22bad22..e14ad47 100644 --- a/app/(app)/layout.tsx +++ b/app/(app)/layout.tsx @@ -3,6 +3,7 @@ import { cookies } from 'next/headers' import { getIronSession } from 'iron-session' import { SessionData, sessionOptions } from '@/lib/session' import { prisma } from '@/lib/prisma' +import { productAccessFilter } from '@/lib/product-access' import { NavBar } from '@/components/shared/nav-bar' import { MinWidthBanner } from '@/components/shared/min-width-banner' import { StatusBar } from '@/components/shared/status-bar' @@ -18,7 +19,7 @@ export default async function AppLayout({ children }: { children: React.ReactNod const [user, userRoles] = await Promise.all([ prisma.user.findUnique({ where: { id: session.userId }, - select: { username: true, email: true }, + select: { username: true, email: true, active_product_id: true }, }), prisma.userRole.findMany({ where: { user_id: session.userId }, @@ -31,6 +32,23 @@ export default async function AppLayout({ children }: { children: React.ReactNod redirect('/login') } + // Resolve active product — clear stale reference if archived or inaccessible + let activeProduct: { id: string; name: string } | null = null + if (user.active_product_id) { + const product = await prisma.product.findFirst({ + where: { id: user.active_product_id, archived: false, ...productAccessFilter(session.userId) }, + select: { id: true, name: true }, + }) + if (product) { + activeProduct = product + } else { + await prisma.user.update({ + where: { id: session.userId }, + data: { active_product_id: null }, + }) + } + } + return (
@@ -42,6 +60,7 @@ export default async function AppLayout({ children }: { children: React.ReactNod userId={session.userId} username={user.username} email={user.email} + activeProduct={activeProduct} />
diff --git a/app/(app)/solo/page.tsx b/app/(app)/solo/page.tsx index 7991495..72623ca 100644 --- a/app/(app)/solo/page.tsx +++ b/app/(app)/solo/page.tsx @@ -1,7 +1,6 @@ import { redirect } from 'next/navigation' import { getSession } from '@/lib/auth' -import { getLastProductCookie } from '@/lib/cookies' -import { getAccessibleProduct, productAccessFilter } from '@/lib/product-access' +import { productAccessFilter } from '@/lib/product-access' import { prisma } from '@/lib/prisma' import { ProductPicker } from '@/components/solo/product-picker' @@ -9,11 +8,17 @@ export default async function SoloPage() { const session = await getSession() if (!session.userId) redirect('/login') - const lastProductId = await getLastProductCookie() + const user = await prisma.user.findUnique({ + where: { id: session.userId }, + select: { active_product_id: true }, + }) - if (lastProductId) { - const product = await getAccessibleProduct(lastProductId, session.userId) - if (product && !product.archived) redirect(`/products/${lastProductId}/solo`) + if (user?.active_product_id) { + const product = await prisma.product.findFirst({ + where: { id: user.active_product_id, archived: false, ...productAccessFilter(session.userId) }, + select: { id: true }, + }) + if (product) redirect(`/products/${user.active_product_id}/solo`) } const products = await prisma.product.findMany({ diff --git a/components/shared/nav-bar.tsx b/components/shared/nav-bar.tsx index f52685a..f7cc8a8 100644 --- a/components/shared/nav-bar.tsx +++ b/components/shared/nav-bar.tsx @@ -15,9 +15,10 @@ interface NavBarProps { userId: string username: string email: string | null + activeProduct: { id: string; name: string } | null } -export function NavBar({ isDemo, roles, userId, username, email }: NavBarProps) { +export function NavBar({ isDemo, roles, userId, username, email, activeProduct: _activeProduct }: NavBarProps) { const pathname = usePathname() const currentProduct = useProductStore(s => s.currentProduct) diff --git a/lib/cookies.ts b/lib/cookies.ts deleted file mode 100644 index c63bdc1..0000000 --- a/lib/cookies.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { cookies } from 'next/headers' - -const LAST_PRODUCT_COOKIE = 'lastProductId' -const THIRTY_DAYS_SECONDS = 60 * 60 * 24 * 30 - -export async function setLastProductCookie(productId: string) { - const store = await cookies() - store.set(LAST_PRODUCT_COOKIE, productId, { - httpOnly: true, - sameSite: 'lax', - maxAge: THIRTY_DAYS_SECONDS, - path: '/', - }) -} - -export async function getLastProductCookie(): Promise { - const store = await cookies() - return store.get(LAST_PRODUCT_COOKIE)?.value ?? null -} diff --git a/proxy.ts b/proxy.ts index 5771204..0a4e228 100644 --- a/proxy.ts +++ b/proxy.ts @@ -5,9 +5,6 @@ import { sessionOptions } from '@/lib/session' const protectedRoutes = ['/dashboard', '/products', '/todos', '/settings', '/solo'] const authRoutes = ['/login', '/register'] -const SOLO_ROUTE = /^\/products\/([^/]+)\/solo$/ -const THIRTY_DAYS_SECONDS = 60 * 60 * 24 * 30 - export function proxy(request: NextRequest) { const path = request.nextUrl.pathname const isProtected = protectedRoutes.some(r => path.startsWith(r)) @@ -24,20 +21,7 @@ export function proxy(request: NextRequest) { return NextResponse.redirect(new URL('/dashboard', request.url)) } - const response = NextResponse.next() - - // Remember last visited product for /solo redirect - const soloMatch = path.match(SOLO_ROUTE) - if (soloMatch) { - response.cookies.set('lastProductId', soloMatch[1], { - httpOnly: true, - sameSite: 'lax', - maxAge: THIRTY_DAYS_SECONDS, - path: '/', - }) - } - - return response + return NextResponse.next() } export const config = {