From 8167acb2317757c1797023ce3a1e1842653296a9 Mon Sep 17 00:00:00 2001 From: Rami Bitar Date: Sat, 1 Aug 2026 16:11:47 -0400 Subject: [PATCH] Add customer accounts: login, signup, reset, activate, orders - Storefront customer operations in graphql/customer.js and a server-safe services/shopify/customer.ts - Session held in an httpOnly, sameSite=lax cookie set by the /api/account handlers; the access token never reaches client JS - Pages: /account/login, /register, /recover, /reset/[id]/[token] and /activate/[id]/[token] for Shopify's emailed links - /account renders order history as master-detail on one screen, since the Storefront API has no standalone order-by-id query for customers - Header user icon: links to sign-in when signed out, otherwise a menu with name, email, order history, and sign out - Login errors are collapsed and password recovery responds identically for known and unknown emails, so neither form enumerates accounts Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016jWbNNJLksC1QG8z8845FX --- app/account/activate/[id]/[token]/page.tsx | 39 ++++ app/account/login/page.tsx | 44 ++++ app/account/page.tsx | 38 ++++ app/account/recover/page.tsx | 29 +++ app/account/register/page.tsx | 49 +++++ app/account/reset/[id]/[token]/page.tsx | 38 ++++ app/api/account/activate/route.ts | 30 +++ app/api/account/login/route.ts | 31 +++ app/api/account/logout/route.ts | 10 + app/api/account/me/route.ts | 19 ++ app/api/account/recover/route.ts | 10 + app/api/account/register/route.ts | 39 ++++ app/api/account/reset/route.ts | 27 +++ components/shopify/account-form.tsx | 142 +++++++++++++ components/shopify/account-menu.tsx | 112 +++++++++++ components/shopify/header.tsx | 2 + components/shopify/order-history.tsx | 166 +++++++++++++++ graphql/customer.js | 173 ++++++++++++++++ services/shopify/customer.ts | 223 +++++++++++++++++++++ services/shopify/session.ts | 31 +++ 20 files changed, 1252 insertions(+) create mode 100644 app/account/activate/[id]/[token]/page.tsx create mode 100644 app/account/login/page.tsx create mode 100644 app/account/page.tsx create mode 100644 app/account/recover/page.tsx create mode 100644 app/account/register/page.tsx create mode 100644 app/account/reset/[id]/[token]/page.tsx create mode 100644 app/api/account/activate/route.ts create mode 100644 app/api/account/login/route.ts create mode 100644 app/api/account/logout/route.ts create mode 100644 app/api/account/me/route.ts create mode 100644 app/api/account/recover/route.ts create mode 100644 app/api/account/register/route.ts create mode 100644 app/api/account/reset/route.ts create mode 100644 components/shopify/account-form.tsx create mode 100644 components/shopify/account-menu.tsx create mode 100644 components/shopify/order-history.tsx create mode 100644 graphql/customer.js create mode 100644 services/shopify/customer.ts create mode 100644 services/shopify/session.ts diff --git a/app/account/activate/[id]/[token]/page.tsx b/app/account/activate/[id]/[token]/page.tsx new file mode 100644 index 0000000..199e1ef --- /dev/null +++ b/app/account/activate/[id]/[token]/page.tsx @@ -0,0 +1,39 @@ +import Header from '@/components/shopify/header'; +import Footer from '@/components/shopify/footer'; +import AccountForm from '@/components/shopify/account-form'; + +export const metadata = { title: 'Activate your account — Shop' }; + +// Shopify's emailed activation link is /account/activate/{id}/{token}. +export default async function Page({ + params, +}: { + params: Promise<{ id: string; token: string }>; +}) { + const { id, token } = await params; + + return ( + <> +
+
+ +
+