@doswiftly/cli 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/deploy.d.ts +20 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +219 -6
- package/dist/commands/deploy.js.map +1 -1
- package/package.json +4 -4
- package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-minimal/wrangler.toml +11 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs/wrangler.toml +11 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +29 -5
- package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/addresses/page.tsx +19 -15
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/error.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +39 -34
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/page.tsx +9 -7
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/page.tsx +13 -9
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +1 -2
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/settings/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +14 -12
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +14 -10
- package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/category-products-client.tsx +4 -2
- package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/error.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +228 -184
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
- package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
- package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
- package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
- package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/error.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +11 -8
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/products-client.tsx +14 -10
- package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +21 -25
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +6 -68
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +14 -12
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +28 -18
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +27 -22
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +48 -43
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +9 -7
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +18 -15
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +8 -7
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +21 -11
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +16 -13
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +12 -9
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +37 -12
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +20 -12
- package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +23 -24
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +4 -1
- package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +8 -6
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +26 -13
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +25 -27
- package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
- package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +40 -7
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +56 -70
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
- package/templates/storefront-nextjs-shadcn/generated/graphql.ts +1159 -551
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +1 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +22 -249
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +3 -3
- package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
- package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
- package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +1 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +41 -8
- package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +20 -18
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +2 -1
- package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
- package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
- package/templates/storefront-nextjs-shadcn/package.json +3 -2
- package/templates/storefront-nextjs-shadcn/proxy.ts +115 -46
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -58
- package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
- /package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { NextConfig } from 'next';
|
|
2
|
+
import createNextIntlPlugin from 'next-intl/plugin';
|
|
3
|
+
|
|
4
|
+
const withNextIntl = createNextIntlPlugin();
|
|
2
5
|
|
|
3
6
|
const nextConfig: NextConfig = {
|
|
4
7
|
// Enable React strict mode for better development experience
|
|
5
8
|
reactStrictMode: true,
|
|
6
9
|
|
|
7
|
-
// Image optimization
|
|
10
|
+
// Image optimization — GraphQL API returns ready-to-use CDN URLs.
|
|
11
|
+
// Use url(transform: { maxWidth: 800 }) in queries. No client-side loader needed.
|
|
8
12
|
images: {
|
|
9
13
|
remotePatterns: [
|
|
10
14
|
{
|
|
@@ -21,11 +25,8 @@ const nextConfig: NextConfig = {
|
|
|
21
25
|
port: '8000',
|
|
22
26
|
},
|
|
23
27
|
],
|
|
24
|
-
// Allow dangerous URLs for local development
|
|
25
28
|
dangerouslyAllowSVG: true,
|
|
26
29
|
contentDispositionType: 'attachment',
|
|
27
|
-
// Disable image optimization for local development if needed
|
|
28
|
-
unoptimized: process.env.NODE_ENV === 'development',
|
|
29
30
|
},
|
|
30
31
|
|
|
31
32
|
// Experimental features
|
|
@@ -44,4 +45,4 @@ const nextConfig: NextConfig = {
|
|
|
44
45
|
},
|
|
45
46
|
};
|
|
46
47
|
|
|
47
|
-
export default nextConfig;
|
|
48
|
+
export default withNextIntl(nextConfig);
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@doswiftly/storefront-operations": "{{STOREFRONT_OPS_VERSION}}",
|
|
16
16
|
"@doswiftly/storefront-sdk": "^4.0.0",
|
|
17
17
|
"@tanstack/react-query": "^5.62.0",
|
|
18
|
-
"next": "^16.
|
|
18
|
+
"next": "^16.1.7",
|
|
19
19
|
"react": "^19",
|
|
20
20
|
"react-dom": "^19",
|
|
21
21
|
"lucide-react": "latest",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"zustand": "^5.0.2",
|
|
35
35
|
"zod": "^3.23.8",
|
|
36
36
|
"sonner": "^1.7.1",
|
|
37
|
+
"next-intl": "^4.1.0",
|
|
37
38
|
"next-themes": "^0.4.4",
|
|
38
39
|
"react-hook-form": "^7.55.0",
|
|
39
40
|
"@hookform/resolvers": "^5.2.0"
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
45
46
|
"graphql": "^16.10.0",
|
|
46
47
|
"@next/env": "^16.0.0",
|
|
47
|
-
"@opennextjs/cloudflare": "^1.
|
|
48
|
+
"@opennextjs/cloudflare": "^1.17.1",
|
|
48
49
|
"@types/node": "latest",
|
|
49
50
|
"@types/react": "latest",
|
|
50
51
|
"@types/react-dom": "latest",
|
|
@@ -1,79 +1,148 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import type { NextRequest } from "next/server";
|
|
3
|
-
import
|
|
3
|
+
import createMiddleware from "next-intl/middleware";
|
|
4
|
+
import { defaultLocale, localePrefix } from "@/i18n/routing";
|
|
5
|
+
import {
|
|
6
|
+
AUTH_COOKIE_NAME,
|
|
7
|
+
LANGUAGE_COOKIE_NAME,
|
|
8
|
+
LANGUAGE_COOKIE_MAX_AGE,
|
|
9
|
+
matchesRoute,
|
|
10
|
+
} from "@doswiftly/storefront-sdk";
|
|
4
11
|
import {
|
|
5
12
|
protectedRoutes,
|
|
6
13
|
guestOnlyRoutes,
|
|
7
14
|
redirects,
|
|
8
15
|
} from "@/lib/auth/routes";
|
|
9
16
|
|
|
17
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
|
|
18
|
+
const shopSlug = process.env.NEXT_PUBLIC_SHOP_SLUG || "demo-shop";
|
|
19
|
+
|
|
10
20
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Security Model (3 layers):
|
|
14
|
-
* 1. Proxy function (this file) - Fast redirect based on cookie existence (UX layer)
|
|
15
|
-
* 2. AuthGuard component - Client-side validation via useAuth hook
|
|
16
|
-
* 3. GraphQL Backend - Ultimate security (validates token on every request)
|
|
17
|
-
*
|
|
18
|
-
* IMPORTANT: This proxy checks cookie EXISTENCE, not VALIDITY.
|
|
19
|
-
* Token validation happens in:
|
|
20
|
-
* - AuthGuard (client-side via useAuth hook)
|
|
21
|
-
* - GraphQL API (server-side, ultimate security)
|
|
21
|
+
* Fetch supported locales from backend (cached 60s).
|
|
22
|
+
* Backend is SSOT — merchant adds/removes language → immediately reflected.
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* - Cannot make GraphQL requests to validate token
|
|
26
|
-
* - Provides instant redirect without waiting for client hydration
|
|
27
|
-
* - Prevents flash of protected content for unauthenticated users
|
|
28
|
-
*
|
|
29
|
-
* @see lib/auth/routes.ts - SSOT for route configuration
|
|
30
|
-
* @see components/auth/auth-guard.tsx - Client-side auth protection
|
|
24
|
+
* In-memory cache is acceptable in middleware context — warm instances benefit,
|
|
25
|
+
* cold starts make one extra backend call (graceful degradation).
|
|
31
26
|
*/
|
|
32
|
-
|
|
27
|
+
let cachedLocales: string[] | null = null;
|
|
28
|
+
let cacheExpiry = 0;
|
|
29
|
+
let cachedIntlMiddleware: ReturnType<typeof createMiddleware> | null = null;
|
|
30
|
+
let cachedLocaleRegex: RegExp | null = null;
|
|
31
|
+
|
|
32
|
+
async function getSupportedLocales(): Promise<string[]> {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (cachedLocales && now < cacheExpiry) {
|
|
35
|
+
return cachedLocales;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(`${apiUrl}/storefront/graphql`, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
"X-Shop-Slug": shopSlug,
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
query: `{ shop { supportedLanguages } }`,
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
const json = await res.json();
|
|
50
|
+
const locales: string[] =
|
|
51
|
+
json?.data?.shop?.supportedLanguages?.filter(Boolean);
|
|
52
|
+
|
|
53
|
+
if (locales && locales.length > 0) {
|
|
54
|
+
cachedLocales = locales;
|
|
55
|
+
cacheExpiry = now + 60_000; // 60s cache
|
|
56
|
+
// Rebuild derived caches when locales change
|
|
57
|
+
cachedIntlMiddleware = createMiddleware({
|
|
58
|
+
locales,
|
|
59
|
+
defaultLocale,
|
|
60
|
+
localePrefix,
|
|
61
|
+
localeCookie: {
|
|
62
|
+
name: LANGUAGE_COOKIE_NAME,
|
|
63
|
+
maxAge: LANGUAGE_COOKIE_MAX_AGE,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
cachedLocaleRegex = new RegExp(
|
|
67
|
+
`^\\/(${locales.join("|")})(\\\/|$)`
|
|
68
|
+
);
|
|
69
|
+
return locales;
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Backend unreachable — use cached or fallback
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return cachedLocales || [defaultLocale];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getIntlMiddleware(locales: string[]) {
|
|
79
|
+
if (cachedIntlMiddleware) return cachedIntlMiddleware;
|
|
80
|
+
cachedIntlMiddleware = createMiddleware({
|
|
81
|
+
locales,
|
|
82
|
+
defaultLocale,
|
|
83
|
+
localePrefix,
|
|
84
|
+
localeCookie: {
|
|
85
|
+
name: LANGUAGE_COOKIE_NAME,
|
|
86
|
+
maxAge: LANGUAGE_COOKIE_MAX_AGE,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
return cachedIntlMiddleware;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getLocaleRegex(locales: string[]) {
|
|
93
|
+
if (cachedLocaleRegex) return cachedLocaleRegex;
|
|
94
|
+
cachedLocaleRegex = new RegExp(`^\\/(${locales.join("|")})(\\\/|$)`);
|
|
95
|
+
return cachedLocaleRegex;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function proxy(request: NextRequest) {
|
|
99
|
+
// Step 1: Get dynamic locales from backend
|
|
100
|
+
const locales = await getSupportedLocales();
|
|
101
|
+
|
|
102
|
+
// Step 2: next-intl locale detection & routing (cached middleware instance)
|
|
103
|
+
const handleI18nRouting = getIntlMiddleware(locales);
|
|
104
|
+
const response = handleI18nRouting(request);
|
|
105
|
+
|
|
106
|
+
// Step 3: Auth checks
|
|
33
107
|
const { pathname } = request.nextUrl;
|
|
34
108
|
const token = request.cookies.get(AUTH_COOKIE_NAME)?.value;
|
|
35
109
|
const hasToken = Boolean(token);
|
|
36
110
|
|
|
37
|
-
//
|
|
38
|
-
|
|
111
|
+
// Strip locale prefix for route matching (cached regex)
|
|
112
|
+
const localePattern = getLocaleRegex(locales);
|
|
113
|
+
const localeMatch = pathname.match(localePattern);
|
|
114
|
+
const urlLocalePrefix = localeMatch ? `/${localeMatch[1]}` : "";
|
|
115
|
+
const pathnameWithoutLocale = pathname.replace(localePattern, "/");
|
|
116
|
+
const matchPathname =
|
|
117
|
+
pathnameWithoutLocale === "" ? "/" : pathnameWithoutLocale;
|
|
118
|
+
|
|
119
|
+
if (matchesRoute(matchPathname, protectedRoutes)) {
|
|
39
120
|
if (!hasToken) {
|
|
40
|
-
const loginUrl = new URL(
|
|
41
|
-
|
|
121
|
+
const loginUrl = new URL(
|
|
122
|
+
`${urlLocalePrefix}${redirects.unauthenticated}`,
|
|
123
|
+
request.url
|
|
124
|
+
);
|
|
42
125
|
loginUrl.searchParams.set("redirect", pathname);
|
|
43
126
|
return NextResponse.redirect(loginUrl);
|
|
44
127
|
}
|
|
45
128
|
}
|
|
46
129
|
|
|
47
|
-
|
|
48
|
-
if (matchesRoute(pathname, guestOnlyRoutes)) {
|
|
130
|
+
if (matchesRoute(matchPathname, guestOnlyRoutes)) {
|
|
49
131
|
if (hasToken) {
|
|
50
132
|
return NextResponse.redirect(
|
|
51
|
-
new URL(
|
|
133
|
+
new URL(
|
|
134
|
+
`${urlLocalePrefix}${redirects.authenticated}`,
|
|
135
|
+
request.url
|
|
136
|
+
)
|
|
52
137
|
);
|
|
53
138
|
}
|
|
54
139
|
}
|
|
55
140
|
|
|
56
|
-
return
|
|
141
|
+
return response;
|
|
57
142
|
}
|
|
58
143
|
|
|
59
|
-
/**
|
|
60
|
-
* Matcher configuration
|
|
61
|
-
*
|
|
62
|
-
* Only run proxy on routes that need auth checks.
|
|
63
|
-
* This improves performance by skipping static assets, API routes, etc.
|
|
64
|
-
*
|
|
65
|
-
* Update this when adding new protected or guest-only route groups.
|
|
66
|
-
*/
|
|
67
144
|
export const config = {
|
|
68
145
|
matcher: [
|
|
69
|
-
|
|
70
|
-
* Match all request paths except:
|
|
71
|
-
* - _next/static (static files)
|
|
72
|
-
* - _next/image (image optimization files)
|
|
73
|
-
* - favicon.ico (favicon file)
|
|
74
|
-
* - public folder files (images, etc.)
|
|
75
|
-
* - API routes (handled separately)
|
|
76
|
-
*/
|
|
77
|
-
"/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
|
|
146
|
+
"/((?!api|trpc|_next|_vercel|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)",
|
|
78
147
|
],
|
|
79
148
|
};
|
|
@@ -1,58 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
setCartId: (cartId: string | null) => {
|
|
26
|
-
set({ cartId });
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
clearCart: () => {
|
|
30
|
-
set({ cartId: null, isOpen: false });
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
openCart: () => {
|
|
34
|
-
set({ isOpen: true });
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
closeCart: () => {
|
|
38
|
-
set({ isOpen: false });
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
toggleCart: () => {
|
|
42
|
-
set((state) => ({ isOpen: !state.isOpen }));
|
|
43
|
-
},
|
|
44
|
-
}),
|
|
45
|
-
{
|
|
46
|
-
name: 'cart-storage',
|
|
47
|
-
// Persist only cartId — server is the source of truth for items
|
|
48
|
-
partialize: (state) => ({ cartId: state.cartId }),
|
|
49
|
-
},
|
|
50
|
-
),
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const {
|
|
55
|
-
Provider: CartProvider,
|
|
56
|
-
useStore: useCartStore,
|
|
57
|
-
useApi: useCartStoreApi,
|
|
58
|
-
} = createStoreContext<CartState>('CartStore');
|
|
1
|
+
/**
|
|
2
|
+
* Cart store — re-export facade from SDK.
|
|
3
|
+
*
|
|
4
|
+
* All cart state management is now in @doswiftly/storefront-sdk/react.
|
|
5
|
+
* This file preserves import compatibility for existing template consumers.
|
|
6
|
+
*/
|
|
7
|
+
export {
|
|
8
|
+
createCartStore,
|
|
9
|
+
CartProvider,
|
|
10
|
+
useCartStore,
|
|
11
|
+
useCartStoreApi,
|
|
12
|
+
selectCartId,
|
|
13
|
+
selectIsCartOpen,
|
|
14
|
+
selectCartIsLoading,
|
|
15
|
+
} from '@doswiftly/storefront-sdk/react';
|
|
16
|
+
|
|
17
|
+
export type {
|
|
18
|
+
CartState,
|
|
19
|
+
CartActions,
|
|
20
|
+
CartStoreConfig,
|
|
21
|
+
CartData,
|
|
22
|
+
CartLineInput,
|
|
23
|
+
CartLineUpdateInput,
|
|
24
|
+
} from '@doswiftly/storefront-sdk/react';
|
|
@@ -11,3 +11,14 @@ assets = { directory = ".open-next/assets", binding = "ASSETS" }
|
|
|
11
11
|
# Cloudflare Images binding for Next.js /_next/image optimization
|
|
12
12
|
[images]
|
|
13
13
|
binding = "IMAGES"
|
|
14
|
+
|
|
15
|
+
# Observability — logs persisted in Cloudflare Dashboard
|
|
16
|
+
[observability]
|
|
17
|
+
enabled = true
|
|
18
|
+
head_sampling_rate = 1
|
|
19
|
+
|
|
20
|
+
[observability.logs]
|
|
21
|
+
enabled = true
|
|
22
|
+
head_sampling_rate = 1
|
|
23
|
+
persist = true
|
|
24
|
+
invocation_logs = true
|
|
File without changes
|
/package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/loading.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/page.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/loading.tsx
RENAMED
|
File without changes
|
/package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/loading.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx
RENAMED
|
File without changes
|
|
File without changes
|