added docker file and migrated db to production db
31
api/Dockerfile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Creating multi-stage build for production
|
||||||
|
FROM node:20-alpine as build
|
||||||
|
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev vips-dev git > /dev/null 2>&1
|
||||||
|
ARG NODE_ENV=production
|
||||||
|
ENV NODE_ENV=${NODE_ENV}
|
||||||
|
|
||||||
|
WORKDIR /opt/
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm install -g node-gyp
|
||||||
|
RUN npm config set fetch-retry-maxtimeout 600000 -g && npm install --only=production
|
||||||
|
ENV PATH=/opt/node_modules/.bin:$PATH
|
||||||
|
WORKDIR /opt/app
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
|
# Creating final production image
|
||||||
|
FROM node:20-alpine
|
||||||
|
RUN apk add --no-cache vips-dev
|
||||||
|
ARG NODE_ENV=production
|
||||||
|
ENV NODE_ENV=${NODE_ENV}
|
||||||
|
WORKDIR /opt/
|
||||||
|
COPY --from=build /opt/node_modules ./node_modules
|
||||||
|
WORKDIR /opt/app
|
||||||
|
COPY --from=build /opt/app ./
|
||||||
|
ENV PATH=/opt/node_modules/.bin:$PATH
|
||||||
|
|
||||||
|
RUN chown -R node:node /opt/app
|
||||||
|
USER node
|
||||||
|
EXPOSE 1337
|
||||||
|
CMD ["npm", "run", "start"]
|
||||||
BIN
api/bun.lockb
Executable file
@ -6,7 +6,7 @@ export default ({ env }) => {
|
|||||||
const connections = {
|
const connections = {
|
||||||
mysql: {
|
mysql: {
|
||||||
connection: {
|
connection: {
|
||||||
host: env('DATABASE_HOST', 'localhost'),
|
host: env('DATABASE_HOST', '192.168.50.107'),
|
||||||
port: env.int('DATABASE_PORT', 3306),
|
port: env.int('DATABASE_PORT', 3306),
|
||||||
database: env('DATABASE_NAME', 'strapi'),
|
database: env('DATABASE_NAME', 'strapi'),
|
||||||
user: env('DATABASE_USERNAME', 'strapi'),
|
user: env('DATABASE_USERNAME', 'strapi'),
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 571 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 842 KiB |
|
Before Width: | Height: | Size: 356 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 682 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 933 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
@ -1,4 +1,5 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
|
// https://stackoverflow.com/questions/76164057/next-js-hostname-not-configured-under-images-in-next-config-js-even-though-remot
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
@ -12,6 +13,22 @@ const nextConfig = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "ih1.redbubble.net",
|
||||||
|
port: "",
|
||||||
|
pathname: "/**",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: "http",
|
||||||
|
hostname: "localhost",
|
||||||
|
port: "1337",
|
||||||
|
pathname: "/**",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, {useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import MenuCard from "@/components/ui/MenuCard/MenuCard";
|
import MenuCard from "@/components/ui/MenuCard/MenuCard";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import Product from "@/../public/batch1/files_list.json";
|
import Product from "@/../public/batch1/files_list.json";
|
||||||
@ -10,9 +10,10 @@ const Products = () => {
|
|||||||
const catID = useParams();
|
const catID = useParams();
|
||||||
const [maxPrice, setMaxPrice] = useState(100);
|
const [maxPrice, setMaxPrice] = useState(100);
|
||||||
const [sort, setSort] = useState("asc");
|
const [sort, setSort] = useState("asc");
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
|
||||||
async function GetData () {
|
useEffect(() => {
|
||||||
// const response = await fetch("/strapi/products?=populate=*", {
|
const fetchData = async () => {
|
||||||
const response = await fetch(process.env.NEXT_PUBLIC_API_URL +"/products?populate=*", {
|
const response = await fetch(process.env.NEXT_PUBLIC_API_URL +"/products?populate=*", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@ -22,14 +23,15 @@ const Products = () => {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
// console.log(data.data);
|
// console.log(data.data);
|
||||||
// console.log(data.data[0].image.url);
|
// console.log(data.data[0].image.url);
|
||||||
return data.data;
|
setData(data.data);
|
||||||
};
|
};
|
||||||
const products = GetData();
|
fetchData();
|
||||||
console.log(products);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="products">
|
<div className="products">
|
||||||
<div className="left">
|
{/* <div className="left">
|
||||||
<div className="filterItem">
|
<div className="filterItem">
|
||||||
<h2>Product Categories</h2>
|
<h2>Product Categories</h2>
|
||||||
<div className="inputItem">
|
<div className="inputItem">
|
||||||
@ -70,7 +72,7 @@ const Products = () => {
|
|||||||
<label htmlFor="desc">Price (Highest first)</label>
|
<label htmlFor="desc">Price (Highest first)</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
<div className="right">
|
<div className="right">
|
||||||
{/* <Image
|
{/* <Image
|
||||||
src="/white_logo.png"
|
src="/white_logo.png"
|
||||||
@ -80,7 +82,7 @@ const Products = () => {
|
|||||||
alt="Profile picture"
|
alt="Profile picture"
|
||||||
/> */}
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
<MenuCard Menu={products} />
|
<MenuCard Menu={data} />
|
||||||
{/* <button onClick={GetData} className="bg-blue-500 text-white px-4 py-2 rounded-md">Get Data</button> */}
|
{/* <button onClick={GetData} className="bg-blue-500 text-white px-4 py-2 rounded-md">Get Data</button> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -19,44 +19,63 @@ const ProductPage = ({ productId }: any) => {
|
|||||||
"https://ih1.redbubble.net/image.2997405810.0746/ssrco,slim_fit_t_shirt,womens,101010:01c5ca27c6,front,square_product,600x600.jpg",
|
"https://ih1.redbubble.net/image.2997405810.0746/ssrco,slim_fit_t_shirt,womens,101010:01c5ca27c6,front,square_product,600x600.jpg",
|
||||||
"https://ih1.redbubble.net/image.2997405810.0746/ssrco,slim_fit_t_shirt,flatlay,101010:01c5ca27c6,front,wide_portrait,750x1000-bg,f8f8f8.jpg",
|
"https://ih1.redbubble.net/image.2997405810.0746/ssrco,slim_fit_t_shirt,flatlay,101010:01c5ca27c6,front,wide_portrait,750x1000-bg,f8f8f8.jpg",
|
||||||
];
|
];
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
const [product, setProduct] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
|
const request = await fetch(process.env.NEXT_PUBLIC_API_URL +"/products?populate=*", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + process.env.NEXT_PUBLIC_API_TOKEN
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const res = await request.json();
|
||||||
|
// console.log(res.data);
|
||||||
|
console.log(res.data[1].image.url);
|
||||||
|
setData(res.data);
|
||||||
|
setProduct(res.data[parseInt(productId)]?.image?.url);
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
}, [productId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="product">
|
<div className="product">
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<div className="images">
|
<div className="images">
|
||||||
<div className="product-image">
|
<div className="product-image">
|
||||||
{/* <Image
|
<Image
|
||||||
src={images[parseInt(productId)]}
|
src={`${process.env.NEXT_PUBLIC_UPLOAD_URL+product}`}
|
||||||
fill
|
fill
|
||||||
className=""
|
className=""
|
||||||
alt="Shirt Mockup"
|
alt="Shirt Mockup"
|
||||||
style={{ objectFit: "cover" }}
|
style={{ objectFit: "cover" }}
|
||||||
onClick={(e) => setSelectedImage(0)}
|
onClick={(e) => setSelectedImage(0)}
|
||||||
/> */}
|
/>
|
||||||
<Image
|
{/* <Image
|
||||||
src={images[0]}
|
src={images[0]}
|
||||||
fill
|
fill
|
||||||
className=""
|
className=""
|
||||||
alt="Shirt Mockup"
|
alt="Shirt Mockup"
|
||||||
style={{ objectFit: "cover" }}
|
style={{ objectFit: "cover" }}
|
||||||
onClick={(e) => setSelectedImage(0)}
|
onClick={(e) => setSelectedImage(0)}
|
||||||
/>
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
<div className="product-image">
|
<div className="product-image">
|
||||||
<Image
|
{/* <Image
|
||||||
src={images[1]}
|
src={images[1]}
|
||||||
fill
|
fill
|
||||||
className=""
|
className=""
|
||||||
alt="Shirt Mockup"
|
alt="Shirt Mockup"
|
||||||
style={{ objectFit: "cover" }}
|
style={{ objectFit: "cover" }}
|
||||||
onClick={(e) => setSelectedImage(1)}
|
onClick={(e) => setSelectedImage(1)}
|
||||||
/>
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mainImage">
|
<div className="mainImage">
|
||||||
<div className="main-image">
|
<div className="main-image">
|
||||||
<Image
|
<Image
|
||||||
src={images[selectedImage]}
|
src={data[selectedImage]}
|
||||||
fill
|
fill
|
||||||
className=""
|
className=""
|
||||||
style={{ objectFit: "cover" }}
|
style={{ objectFit: "cover" }}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const MenuCard = ({ Menu }: any) => {
|
|||||||
<div className="w-full h-[75%]">
|
<div className="w-full h-[75%]">
|
||||||
<div className="h-full relative w-full">
|
<div className="h-full relative w-full">
|
||||||
<Image
|
<Image
|
||||||
src={`/${process.env.NEXT_PUBLIC_UPLOAD_URL+menu_item.image.url}`}
|
src={`${process.env.NEXT_PUBLIC_UPLOAD_URL+menu_item?.image?.url}`}
|
||||||
alt="product photo"
|
alt="product photo"
|
||||||
fill
|
fill
|
||||||
style={{ objectFit: "cover", objectPosition: "center" }}
|
style={{ objectFit: "cover", objectPosition: "center" }}
|
||||||
|
|||||||