Skip to content

Commit b2332df

Browse files
Copilotsyed-reza98
andcommitted
fix: address PR review feedback for subdomain routing
- Remove duplicate comment in prisma/schema.sqlite.prisma - Add page number validation with proper NaN and negative value handling - Use proper Prisma types (ProductWhereInput, ProductOrderByWithRelationInput) - Add search submit button for accessibility - Fix label htmlFor attribute issue (changed to span for sort controls) - Add error logging in middleware for store lookup failures - Return consistent error format in API 404 response - Improve custom domain detection with platform domain whitelist - Replace native img tags with Next.js Image component for optimization - Extend static file pattern matching to include more file types Co-authored-by: syed-reza98 <71028588+syed-reza98@users.noreply.github.com>
1 parent f381a1a commit b2332df

File tree

6 files changed

+64
-30
lines changed

6 files changed

+64
-30
lines changed

middleware.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,33 @@ function extractSubdomain(hostname: string): string | null {
6666
}
6767

6868
/**
69-
* Check if hostname is a potential custom domain (not a subdomain)
69+
* List of known platform domains
70+
* Custom domains are any domain not in this list
71+
*/
72+
const PLATFORM_DOMAINS = [
73+
"localhost",
74+
"stormcom.app",
75+
"stormcom.com",
76+
"vercel.app",
77+
];
78+
79+
/**
80+
* Check if hostname is a potential custom domain (not a subdomain of platform)
7081
*/
7182
function isCustomDomain(hostname: string): boolean {
7283
const host = hostname.split(":")[0];
7384

74-
// Not localhost
75-
if (host === "localhost" || host.endsWith(".localhost")) {
85+
// Not localhost or platform domain
86+
if (
87+
host === "localhost" ||
88+
host.endsWith(".localhost") ||
89+
PLATFORM_DOMAINS.some((domain) => host === domain || host.endsWith("." + domain))
90+
) {
7691
return false;
7792
}
78-
79-
// Custom domain: vendor.com (2 parts)
80-
const parts = host.split(".");
81-
return parts.length === 2;
93+
94+
// Anything not matching platform domains is considered a custom domain
95+
return true;
8296
}
8397

8498
/**
@@ -112,9 +126,9 @@ function shouldSkipSubdomainRouting(
112126
// Skip Next.js internal routes
113127
if (pathname.startsWith("/_next")) return true;
114128

115-
// Skip static files (common file extensions)
129+
// Skip static files (common file extensions; extend as needed)
116130
if (pathname.startsWith("/favicon")) return true;
117-
if (pathname.match(/\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|webp|avif|json|xml|txt|map)$/i)) return true;
131+
if (pathname.match(/\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|webp|avif|json|xml|txt|map|webmanifest|pdf)$/i)) return true;
118132

119133
// Skip checkout routes
120134
if (pathname.startsWith("/checkout")) return true;
@@ -170,8 +184,9 @@ async function getStoreBySubdomainOrDomain(
170184
}
171185

172186
return null;
173-
} catch {
187+
} catch (err) {
174188
// Don't block on cache/fetch errors - allow request to continue
189+
console.error("[middleware] Store lookup failed:", err);
175190
return null;
176191
}
177192
}

prisma/schema.sqlite.prisma

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ enum SubscriptionStatus {
212212
PAUSED
213213
}
214214

215-
// Store model (E-commerce tenant - extends Organization)
216215
// Store model (E-commerce tenant - extends Organization)
217216
model Store {
218217
id String @id @default(cuid())

src/app/api/stores/lookup/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function GET(request: NextRequest) {
4949
});
5050

5151
if (!store) {
52-
return NextResponse.json(null, { status: 404 });
52+
return NextResponse.json({ error: "Store not found" }, { status: 404 });
5353
}
5454

5555
return NextResponse.json({

src/app/store/[slug]/layout.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { headers } from "next/headers";
22
import { notFound } from "next/navigation";
33
import prisma from "@/lib/prisma";
44
import Link from "next/link";
5+
import Image from "next/image";
56
import type { Metadata } from "next";
67

78
interface StoreLayoutProps {
@@ -90,10 +91,13 @@ export default async function StoreLayout({
9091
<div className="flex items-center justify-between">
9192
<div className="flex items-center gap-4">
9293
{store.logo && (
93-
<img
94+
<Image
9495
src={store.logo}
9596
alt={store.name}
96-
className="h-10 w-auto"
97+
width={40}
98+
height={40}
99+
className="h-10 w-auto object-contain"
100+
unoptimized
97101
/>
98102
)}
99103
<h1 className="text-2xl font-bold">{store.name}</h1>

src/app/store/[slug]/page.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { headers } from "next/headers";
22
import prisma from "@/lib/prisma";
33
import { getProductImageUrl } from "@/lib/utils";
44
import Link from "next/link";
5+
import Image from "next/image";
56
import { notFound } from "next/navigation";
67

78
interface StoreHomePageProps {
@@ -106,11 +107,13 @@ export default async function StoreHomePage({ params }: StoreHomePageProps) {
106107
className="group relative rounded-lg overflow-hidden border hover:border-primary transition-colors"
107108
>
108109
{category.image ? (
109-
<div className="aspect-square bg-muted">
110-
<img
110+
<div className="aspect-square bg-muted relative">
111+
<Image
111112
src={category.image}
112113
alt={category.name}
113-
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
114+
fill
115+
className="object-cover group-hover:scale-105 transition-transform"
116+
unoptimized
114117
/>
115118
</div>
116119
) : (
@@ -158,10 +161,12 @@ export default async function StoreHomePage({ params }: StoreHomePageProps) {
158161
{/* Product Image */}
159162
<div className="aspect-square bg-muted relative overflow-hidden">
160163
{imageUrl ? (
161-
<img
164+
<Image
162165
src={imageUrl}
163166
alt={product.name}
164-
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
167+
fill
168+
className="object-cover group-hover:scale-105 transition-transform"
169+
unoptimized
165170
/>
166171
) : (
167172
<div className="w-full h-full flex items-center justify-center">

src/app/store/[slug]/products/page.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { headers } from "next/headers";
22
import prisma from "@/lib/prisma";
33
import { getProductImageUrl } from "@/lib/utils";
44
import Link from "next/link";
5+
import Image from "next/image";
56
import { notFound } from "next/navigation";
67
import type { Metadata } from "next";
8+
import type { Prisma } from "@prisma/client";
79

810
interface StoreProductsPageProps {
911
params: Promise<{ slug: string }>;
@@ -44,8 +46,9 @@ export default async function StoreProductsPage({
4446
notFound();
4547
}
4648

47-
// Parse pagination and filters from search params
48-
const page = typeof search.page === "string" ? parseInt(search.page) : 1;
49+
// Parse pagination and filters from search params with validation
50+
const pageNum = typeof search.page === "string" ? parseInt(search.page) : 1;
51+
const page = !isNaN(pageNum) && pageNum > 0 ? pageNum : 1;
4952
const limit = 12;
5053
const skip = (page - 1) * limit;
5154

@@ -54,8 +57,8 @@ export default async function StoreProductsPage({
5457
const sortBy = typeof search.sort === "string" ? search.sort : "newest";
5558
const searchQuery = typeof search.q === "string" ? search.q : undefined;
5659

57-
// Build where clause
58-
const whereClause: Record<string, unknown> = {
60+
// Build where clause with proper Prisma types
61+
const whereClause: Prisma.ProductWhereInput = {
5962
storeId: store.id,
6063
status: "ACTIVE",
6164
deletedAt: null,
@@ -88,8 +91,8 @@ export default async function StoreProductsPage({
8891
];
8992
}
9093

91-
// Build order by
92-
let orderBy: Record<string, string> = { createdAt: "desc" };
94+
// Build order by with proper Prisma type
95+
let orderBy: Prisma.ProductOrderByWithRelationInput = { createdAt: "desc" };
9396
if (sortBy === "price-asc") {
9497
orderBy = { price: "asc" };
9598
} else if (sortBy === "price-desc") {
@@ -173,7 +176,7 @@ export default async function StoreProductsPage({
173176
<label htmlFor="search" className="text-sm font-medium mb-2 block">
174177
Search
175178
</label>
176-
<form action={`/store/${store.slug}/products`} method="get">
179+
<form action={`/store/${store.slug}/products`} method="get" className="flex flex-col gap-2">
177180
<input
178181
type="text"
179182
id="search"
@@ -182,6 +185,12 @@ export default async function StoreProductsPage({
182185
placeholder="Search products..."
183186
className="w-full px-3 py-2 border rounded-md text-sm"
184187
/>
188+
<button
189+
type="submit"
190+
className="w-full px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm hover:bg-primary/90"
191+
>
192+
Search
193+
</button>
185194
</form>
186195
</div>
187196

@@ -253,9 +262,9 @@ export default async function StoreProductsPage({
253262

254263
{/* Sort */}
255264
<div className="flex items-center gap-2">
256-
<label htmlFor="sort" className="text-sm text-muted-foreground">
265+
<span className="text-sm text-muted-foreground">
257266
Sort by:
258-
</label>
267+
</span>
259268
<div className="flex gap-2">
260269
<Link
261270
href={buildFilterUrl({ sort: "newest" })}
@@ -311,10 +320,12 @@ export default async function StoreProductsPage({
311320
>
312321
<div className="aspect-square bg-muted relative overflow-hidden">
313322
{imageUrl ? (
314-
<img
323+
<Image
315324
src={imageUrl}
316325
alt={product.name}
317-
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
326+
fill
327+
className="object-cover group-hover:scale-105 transition-transform"
328+
unoptimized
318329
/>
319330
) : (
320331
<div className="w-full h-full flex items-center justify-center">

0 commit comments

Comments
 (0)