feat: update product categories data structure and implement sitemap generation

- Refactored product categories to use a new data structure.
- Updated API routes to fetch product categories from the new data source.
- Implemented sitemap generation script using dotenv for environment variables.
- Added sitemap generation endpoint to the API.
- Removed old sitemap file and replaced it with a dynamically generated one.
This commit is contained in:
2025-06-11 15:42:49 +03:30
parent 2a132d6934
commit a6da045e19
13 changed files with 633 additions and 53 deletions
+3
View File
@@ -39,3 +39,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
contactMessages.json
/public/sitemap.xml
+197 -13
View File
@@ -23,6 +23,7 @@
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"dotenv": "^16.5.0",
"eslint": "^9",
"eslint-config-next": "15.3.3",
"eslint-config-prettier": "^10.1.5",
@@ -32,7 +33,9 @@
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.12",
"tailwindcss": "^4",
"typescript": "^5"
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.8.3"
}
},
"node_modules/@alloc/quick-lru": {
@@ -66,6 +69,28 @@
"integrity": "sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==",
"dev": true
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@emnapi/core": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
@@ -1281,6 +1306,30 @@
"tailwindcss": "4.1.8"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"node_modules/@tybys/wasm-util": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
@@ -1865,6 +1914,18 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
"dependencies": {
"acorn": "^8.11.0"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1896,6 +1957,12 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2325,6 +2392,12 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2481,6 +2554,15 @@
"node": ">=8"
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -2493,6 +2575,18 @@
"node": ">=0.10.0"
}
},
"node_modules/dotenv": {
"version": "16.5.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2926,6 +3020,18 @@
"ms": "^2.1.1"
}
},
"node_modules/eslint-plugin-import/node_modules/json5": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/eslint-plugin-import/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -2935,6 +3041,18 @@
"semver": "bin/semver.js"
}
},
"node_modules/eslint-plugin-import/node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
"integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
@@ -4082,15 +4200,15 @@
"dev": true
},
"node_modules/json5": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/jsx-ast-utils": {
@@ -4424,6 +4542,12 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -5881,16 +6005,61 @@
"typescript": ">=4.8.4"
}
},
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
"integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.2",
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tslib": {
@@ -6075,6 +6244,12 @@
"react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6193,6 +6368,15 @@
"node": ">=18"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+6 -2
View File
@@ -7,7 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"sitemap": "next-sitemap"
"sitemap": "ts-node scripts/generate-sitemap.ts",
"postbuild": "npm run sitemap"
},
"dependencies": {
"axios": "^1.9.0",
@@ -25,6 +26,7 @@
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"dotenv": "^16.5.0",
"eslint": "^9",
"eslint-config-next": "15.3.3",
"eslint-config-prettier": "^10.1.5",
@@ -34,6 +36,8 @@
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.12",
"tailwindcss": "^4",
"typescript": "^5"
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.8.3"
}
}
-3
View File
@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</sitemapindex>
+311
View File
@@ -0,0 +1,311 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://localhost:3000/en/</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/about-us</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/about-us</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/contact-us</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/contact-us</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/blog</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/blog</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/projects</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/projects</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/1</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/2</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/3</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/en/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>http://localhost:3000/fa/product-categories/40</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
+36
View File
@@ -0,0 +1,36 @@
// scripts/generate-sitemap.ts
const fs = require('fs');
const path = require('path');
const http = require('http');
require('dotenv').config();
const API_PATH = '/api/sitemapBuilder';
const DEST_PATH = path.join(process.cwd(), 'public', 'sitemap.xml');
function fetchSitemap(): Promise<string> {
return new Promise((resolve, reject) => {
http
.get(
`${process.env.VERCEL_PROJECT_PRODUCTION_URL}${API_PATH}`,
(res: any) => {
let data = '';
res.on('data', (chunk: any) => (data += chunk));
res.on('end', () => resolve(data));
},
)
.on('error', reject);
});
}
(async () => {
try {
const xml = await fetchSitemap();
fs.writeFileSync(DEST_PATH, xml, 'utf8');
console.log('✅ Sitemap generated at public/sitemap.xml');
} catch (err) {
console.error('❌ Failed to generate sitemap:', err);
process.exit(1);
}
})();
@@ -1,5 +1,5 @@
import { productCategories } from '../../data';
import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d';
import { productCategoriesData } from '../../data';
// Helper to generate 50 products for a category
export function generateProductsForCategory(
@@ -18,7 +18,7 @@ export function generateProductsForCategory(
// Map of categoryId to products
export const productsByCategory: Record<string, IProductCardProps[]> =
Object.fromEntries(
productCategories.map((cat) => [
productCategoriesData.map((cat) => [
cat.id,
generateProductsForCategory(cat.id, cat.title),
]),
@@ -1,7 +1,7 @@
import { NextResponse, NextRequest } from 'next/server';
import { cookies } from 'next/headers';
import { productCategories } from '../../data';
import { generateProductsForCategory } from './data';
import { productCategories } from '@/app/api/project_categories/data';
export async function GET(request: NextRequest, context: any) {
const cookieStore = await cookies();
+2 -2
View File
@@ -2,7 +2,7 @@
import { NextResponse, NextRequest } from 'next/server';
import { cookies } from 'next/headers';
import { productCategories } from '../data';
import { productCategoriesData } from '../data';
interface IContext {
params: {
@@ -20,7 +20,7 @@ export async function GET(request: NextRequest, context: any) {
const { id } = params;
let category = productCategories.find((cat) => cat.id === id);
let category = productCategoriesData.find((cat) => cat.id === id);
if (!category) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
+2 -26
View File
@@ -1,4 +1,4 @@
export const productCategories = [
export const productCategoriesData = [
{
id: '1',
title: 'Ghazaghi',
@@ -91,28 +91,4 @@ Facing bricks, also known as face bricks or veneer bricks, are specially designe
Facing Bricks: Characteristics and Features`,
},
] as APIProductCategoryEntity[];
interface APIProductCategoryEntity {
id: string;
title: string;
fa_title?: string;
icon: string;
typesCount: number;
priceTypeTitle: string;
fa_priceTypeTitle?: string;
bestForTitle: string;
fa_bestForTitle?: string;
fa_description?: `Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features`;
description?: `Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features`;
}
];
+5 -4
View File
@@ -1,15 +1,16 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { productCategories } from './data';
import { APIProductCategoryEntity } from '@/models/api';
import { productCategoriesData } from './data';
export async function GET(request: Request) {
let data = productCategories;
let data = productCategoriesData as APIProductCategoryEntity[];
const cookieStore = await cookies();
const lang = (await cookieStore).get('lang')?.value || 'fa';
if (lang === 'fa') {
data = productCategories.map(
data = productCategoriesData.map(
({
id,
title,
@@ -47,5 +48,5 @@ export async function GET(request: Request) {
);
}
return NextResponse.json({ data: productCategories });
return NextResponse.json({ data });
}
+55
View File
@@ -0,0 +1,55 @@
'use server';
// app/(api)/sitemap/route.ts
import { NextResponse } from 'next/server';
import { productCategoriesData } from '../product_categories/data';
const HOST = 'http://localhost:3000';
const pages = [
'/',
'/about-us',
'/contact-us',
'/blog',
'/product-categories',
'/projects',
];
const locales = ['en', 'fa'];
export async function GET() {
try {
pages.push(
...productCategoriesData.map(
(productCategory) => `/product-categories/${productCategory.id}`,
),
);
const urls = pages.flatMap((page) =>
locales.map((locale) => {
const loc = `/${locale}`;
return `
<url>
<loc>${HOST}${loc}${page}</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
`;
}),
);
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.join('\n')}
</urlset>`;
return new NextResponse(xml, {
headers: {
'Content-Type': 'application/xml',
},
});
} catch (err) {
console.error('Sitemap generation failed:', err);
return new NextResponse('Internal Server Errorsssssss', { status: 500 });
}
}
+13
View File
@@ -0,0 +1,13 @@
export interface APIProductCategoryEntity {
id: string;
title: string;
fa_title?: string;
icon: string;
typesCount: number;
priceTypeTitle: string;
fa_priceTypeTitle?: string;
bestForTitle: string;
fa_bestForTitle?: string;
fa_description?: string;
description?: string;
}