Root cause of the home-page 500 ("اطلاعات برای اعتبارسنجی ارسال نشده است",
401 not_authenticated): getSession() only decrypted the session cookie, it
never checked whether the access token inside it had actually expired. The
JWE wrapper lives 30 days; the real Gooyal access token lives ~10 hours
(expires_in: 36000). So isAuthenticated stayed true long after the token
died, the home page called getCart() anyway, winofyFetch's refresh attempt
failed silently (dead refresh token) and returned no Authorization header
at all, and the resulting 401 was never caught -- crashing the whole page.
Fix: getValidSession() (session.ts) is now the single source of truth --
refreshes when possible, self-heals by clearing the cookie when refresh
fails, deduped per-request via React's cache(). winofyFetch and every
isAuthenticated check (home page, shop layout) now use it instead of the
raw cookie read.
That surfaced a second bug: Next.js forbids writing cookies during a plain
Server Component render (Server Actions/Route Handlers only), so
getValidSession()'s self-heal itself crashed when called from a page like
home. createSession()/deleteSession() now swallow that specific failure --
the refreshed/cleared session is still correct for the rest of the current
request, it just won't persist when called from a context that can't write
cookies (the next request re-derives the same correct answer).
Also added requireSession(path) and wired it into every auth-required page
(cart, checkout, addresses, orders, order-groups/[uuid], profile) --
proxy.ts's gate is deliberately optimistic (cookie presence only, per
Next.js's own guidance), so a present-but-dead session was reaching these
pages and crashing the same way; they now redirect to /login instead.
Verified against the exact failure: a session with a dead access+refresh
token now renders the home page as logged-out (200, not 500) and redirects
/cart to /login (307) instead of crashing. Also verified the happy path
(a genuinely fresh, valid session from a real OTP login) still works.
Shows a clear/deselect row (only when a location is actually set) that
wipes the location cookie and returns to the unfiltered all-stores view.
Also highlights the currently selected neighborhood in the suggested/
results list with a checkmark, since it's now shown there too.
icon/image/logo/cover_image are now MinIO object keys, not URLs -- every
serializer with one of these fields now returns a <field>_url sibling
that's an already-absolute, freshly presigned URL. Added those fields to
every affected type (StoreCategory, StoreListItem, ProductCategory,
ProductListItem) and switched every <img> call site to use the _url field
directly.
Deleted resolveMediaUrl/resolveClientMediaUrl and the
NEXT_PUBLIC_WINOFY_MEDIA_ORIGIN env var -- they existed only to turn
relative paths under the Winofy origin into absolute URLs, which no longer
applies now that media lives on a different host (MinIO) and comes back
pre-resolved.
Also fixes cart-item-row.tsx's product thumbnail, which had been commented
out (broken under the old relative-path assumption).
Restored image_url as a required non-optional field per the doc rather
than treating it as possibly-missing, since every serializer covered by
this migration always includes it (null when no image, not omitted).
Implements winofyfrontenddoc (1).md §3-4: unified guest-home/authenticated-home
screen (header with login chip vs cart badge, pill search bar, dismissible
location-reminder banner, category chips, store cards, interleaved seller
promo card, sticky bottom nav), plus the full post-OTP location-onboarding
sub-flow (geolocation permission -> error/retry -> manual neighborhood
search, unified idle/results/empty states per the doc's own recommendation).
Location state persists in a plain (non-httpOnly) cookie readable both
client- and server-side, since it drives SSR store queries (neighborhood
or lat/lng) as well as client interactions.
Moved cart/checkout/store/product/orders/addresses under a new (shop) route
group so the new home-shell header doesn't double up with the existing
simple nav header on inner pages.
Real bug caught via live staging data: Neighborhood.city is a nested
{uuid, name, slug} object, not a bare uuid string as typed -- broke both
the address form's city filter and the new neighborhood search. Fixed and
verified against real API responses, not just assumed from the guide.
Replaces placeholder colors/type-scale with the documented Figma design
system (§1), and rebuilds login as three screens matching the doc exactly:
role selection, phone entry, and 6-box OTP verification with countdown
resend. Fixes a real bug along the way: normalizeIranianPhone stripped
Persian-digit keyboard input entirely instead of converting it (\D matches
non-ASCII digits too) -- added toLatinDigits/toPersianDigits and verified
the round-trip in isolation.
- Session stored as an encrypted (JWE) httpOnly cookie; access/refresh tokens
never reach the client, client_secret never leaves the server.
- winofyFetch: server-side API client matching FRONTEND_GUIDE.md's plain-success
/ wrapped-error (§4.1-4.2) convention, with auto token refresh.
- Login page (phone -> OTP) wired to Gooyal accounts staging; request_otp
verified working end-to-end. Token exchange returns invalid_client with the
client_id/secret currently on hand — needs a fix from whoever issued them.
- proxy.ts (Next 16's renamed middleware) gates seller/cart/checkout/orders
routes, verified redirecting unauthenticated requests to /login.
- Customer home page renders real store/category data fetched live from
winofy-staging.winsoo.ir.