002f14ced0
Shopping list add already worked generically for batch-cook recipes — no code needed there. New: mark a specific batch-cook dish as "cooked today", track its fridge expiry (cookingHistory.batchDishId), surface a "Leftovers expiring soon" widget on the pantry page, and send a daily push+email reminder via a new /api/internal/cron/leftover-reminders endpoint (mirrors the weekly-digest cron pattern; doesn't use the social notifications table, which requires a non-null actor and isn't built for self-reminders). Also fixes, from user-reported bugs: - Recipe cards showed no batch-cook badge/dish-count/prep-time in some views — added dishCount + prepMins/cookMins (now generated by the AI and persisted) to the card component and /recipes query. - Batch-cook descriptions occasionally contained raw markdown (**bold**) — added explicit "plain prose only" prompt instructions and a stripMarkdown() defensive fallback at render time. - Truncated/cut-off descriptions — the generateObject call had no maxOutputTokens set, so long structured responses could get cut off mid-field; now capped explicitly at 8000. - Generate dialogs (batch-cook + the main AI dialog) could show buttons unreachable once the progress bar appeared mid-generation — restructured so the action row is pinned outside the scrollable content area, not affected by content height changes. - /api/internal/* routes were being redirected to /login by middleware before their own CRON_SECRET check ever ran (pre-existing bug, affected the weekly-digest cron too) — added to PUBLIC_PATHS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getSessionCookie } from "better-auth/cookies";
|
|
|
|
const PUBLIC_PATHS = ["/login", "/signup", "/verify-email", "/forgot-password", "/reset-password", "/api/auth", "/r/", "/u/", "/docs", "/api/v1/openapi.json", "/api/webhooks", "/api/v1/invites/", "/api/internal/"];
|
|
const ADMIN_PATHS = ["/admin"];
|
|
|
|
export async function proxy(request: NextRequest) {
|
|
const { pathname } = request.nextUrl;
|
|
|
|
const isPublic = PUBLIC_PATHS.some((p) => pathname.startsWith(p));
|
|
const isAdmin = ADMIN_PATHS.some((p) => pathname.startsWith(p));
|
|
const isApi = pathname.startsWith("/api/v1");
|
|
|
|
if (isPublic) return NextResponse.next();
|
|
|
|
const sessionCookie = getSessionCookie(request);
|
|
|
|
if (!sessionCookie) {
|
|
if (isApi) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
return NextResponse.redirect(new URL("/login", request.url));
|
|
}
|
|
|
|
if (isAdmin) {
|
|
// Role check happens inside admin pages — middleware only checks auth
|
|
// Full role verification requires DB lookup, done server-side in admin layout
|
|
}
|
|
|
|
return NextResponse.next();
|
|
}
|
|
|
|
export default proxy;
|
|
|
|
export const config = {
|
|
matcher: [
|
|
"/((?!_next/static|_next/image|favicon.ico|icon.svg|icon-192.svg|icon-512.svg|manifest.webmanifest|sw.js|public/).*)",
|
|
],
|
|
};
|