depoly
This commit is contained in:
parent
3272da0b2b
commit
bd0f852f8a
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
RUN npm install --save-dev
|
||||||
|
|
||||||
|
# Build the Next.js app
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Expose the port the app will run on
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["npm", "run", "dev"]
|
BIN
app/favicon.ico
BIN
app/favicon.ico
Binary file not shown.
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 43 KiB |
|
@ -13,7 +13,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
|
||||||
<div className='lg:grid lg:grid-cols-2 w-[80vw] mb-28'>
|
<div className='lg:grid lg:grid-cols-2 w-[80vw] mb-28'>
|
||||||
<div className="flex col-span-1 h-auto w-full ">
|
<div className="flex col-span-1 h-auto w-full ">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${item.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${item.image}`}
|
||||||
alt="Course Image"
|
alt="Course Image"
|
||||||
className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md"
|
className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md"
|
||||||
style={{ aspectRatio: '1 / 1' }}
|
style={{ aspectRatio: '1 / 1' }}
|
||||||
|
@ -28,7 +28,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
|
||||||
{item.description}
|
{item.description}
|
||||||
</p> */}
|
</p> */}
|
||||||
<div
|
<div
|
||||||
className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]'
|
className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
|
||||||
dangerouslySetInnerHTML={{ __html: item.description }}
|
dangerouslySetInnerHTML={{ __html: item.description }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex col-span-1 h-auto w-full justify-end ">
|
<div className="flex col-span-1 h-auto w-full justify-end ">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${item.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${item.image}`}
|
||||||
alt="Course Image"
|
alt="Course Image"
|
||||||
className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md"
|
className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md"
|
||||||
style={{ aspectRatio: '1 / 1' }}
|
style={{ aspectRatio: '1 / 1' }}
|
||||||
|
@ -68,7 +68,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
|
||||||
<div className="flex flex-col relative w-[80vw] lg:mb-28 mb-10 lg:hidden">
|
<div className="flex flex-col relative w-[80vw] lg:mb-28 mb-10 lg:hidden">
|
||||||
<div className="flex col-span-1 h-auto w-full ">
|
<div className="flex col-span-1 h-auto w-full ">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${item.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${item.image}`}
|
||||||
alt="Course Image"
|
alt="Course Image"
|
||||||
className="w-full h-[44vw] object-cover object-center rounded-lg shadow-md"
|
className="w-full h-[44vw] object-cover object-center rounded-lg shadow-md"
|
||||||
style={{ aspectRatio: '1 / 1' }}
|
style={{ aspectRatio: '1 / 1' }}
|
||||||
|
@ -82,7 +82,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
|
||||||
{item.description}
|
{item.description}
|
||||||
</p> */}
|
</p> */}
|
||||||
<div
|
<div
|
||||||
className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]'
|
className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
|
||||||
dangerouslySetInnerHTML={{ __html: item.description }}
|
dangerouslySetInnerHTML={{ __html: item.description }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ const Map = ({ settings }: { settings: SettingsProps }) => {
|
||||||
<p>
|
<p>
|
||||||
{"地址"}
|
{"地址"}
|
||||||
</p>
|
</p>
|
||||||
<button className='text-gray-700 hover:text-mainColor'
|
<button className='text-gray-700 hover:text-mainColor text-left'
|
||||||
onClick={() => { window.open(`https://maps.google.com/?q=${settings.latitude},${settings.longitude}`, '_blank') }}>
|
onClick={() => { window.open(`https://maps.google.com/?q=${settings.latitude},${settings.longitude}`, '_blank') }}>
|
||||||
{settings.address}
|
{settings.address}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -113,7 +113,7 @@ const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, i
|
||||||
{info_images.map((image, index) => (
|
{info_images.map((image, index) => (
|
||||||
<div key={index} className="relative w-full h-[13vw] px-1 transition-all justify-items-center ">
|
<div key={index} className="relative w-full h-[13vw] px-1 transition-all justify-items-center ">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${image.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
|
||||||
alt={`Course Image ${index + 1}`}
|
alt={`Course Image ${index + 1}`}
|
||||||
className="w-full h-full object-cover object-center "
|
className="w-full h-full object-cover object-center "
|
||||||
/>
|
/>
|
||||||
|
@ -128,7 +128,7 @@ const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, i
|
||||||
<div key={index} className="px-4">
|
<div key={index} className="px-4">
|
||||||
<div key={index} className="relative h-[50vw] transition-all justify-items-center mb-16">
|
<div key={index} className="relative h-[50vw] transition-all justify-items-center mb-16">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${image.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
|
||||||
alt={`Course Image ${index + 1}`}
|
alt={`Course Image ${index + 1}`}
|
||||||
className="w-full h-full object-cover object-center "
|
className="w-full h-full object-cover object-center "
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -107,7 +107,7 @@ const CourseImagesSilder = ({ courseData }: { courseData: CoursesProps }) => {
|
||||||
{courseData.images.map((image, index) => (
|
{courseData.images.map((image, index) => (
|
||||||
<div key={index} className="relative w-full h-[35vw] transition-all justify-items-center mb-28 ">
|
<div key={index} className="relative w-full h-[35vw] transition-all justify-items-center mb-28 ">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${image.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
|
||||||
alt={`Course Image ${index + 1}`}
|
alt={`Course Image ${index + 1}`}
|
||||||
className="w-full h-full object-cover object-center rounded-xl "
|
className="w-full h-full object-cover object-center rounded-xl "
|
||||||
/>
|
/>
|
||||||
|
@ -127,7 +127,7 @@ const CourseImagesSilder = ({ courseData }: { courseData: CoursesProps }) => {
|
||||||
<div key={index} className="px-4">
|
<div key={index} className="px-4">
|
||||||
<div key={index} className="relative h-[38vw] transition-all justify-items-center mb-16">
|
<div key={index} className="relative h-[38vw] transition-all justify-items-center mb-16">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${image.image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
|
||||||
alt={`Course Image ${index + 1}`}
|
alt={`Course Image ${index + 1}`}
|
||||||
className="w-full h-full object-cover object-center rounded-xl"
|
className="w-full h-full object-cover object-center rounded-xl"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -8,7 +8,7 @@ const LongDesc = ({ courseData }: { courseData: CoursesProps }) => {
|
||||||
{courseData.title}
|
{courseData.title}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
className='mt-10 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!text-center'
|
className='mt-10 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!text-center [&_*]:!bg-transparent'
|
||||||
dangerouslySetInnerHTML={{ __html: courseData.long_description }}
|
dangerouslySetInnerHTML={{ __html: courseData.long_description }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,7 +49,7 @@ const CoursesSilder = ({ courses }: { courses: CoursesProps[] }) => {
|
||||||
<div className="hidden lg:grid lg:grid-cols-2 w-full mb-28">
|
<div className="hidden lg:grid lg:grid-cols-2 w-full mb-28">
|
||||||
<div className="flex col-span-1 h-auto justify-self-end mr-20">
|
<div className="flex col-span-1 h-auto justify-self-end mr-20">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${course.images[0].image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${course.images[0].image}`}
|
||||||
alt="Course Image"
|
alt="Course Image"
|
||||||
className="w-[50vh] h-[70vh] object-cover object-center rounded-3xl shadow-md"
|
className="w-[50vh] h-[70vh] object-cover object-center rounded-3xl shadow-md"
|
||||||
style={{ aspectRatio: '1 / 1' }}
|
style={{ aspectRatio: '1 / 1' }}
|
||||||
|
@ -77,7 +77,7 @@ const CoursesSilder = ({ courses }: { courses: CoursesProps[] }) => {
|
||||||
|
|
||||||
<div className=" relative w-[50vh] h-[70vh] max-sm:w-[36vh] max-sm:h-[60vh] mt-20 mb-28 overflow-hidden rounded-3xl shadow-md sm:block md:block lg:hidden xl:hidden 2xl:hidden mx-auto">
|
<div className=" relative w-[50vh] h-[70vh] max-sm:w-[36vh] max-sm:h-[60vh] mt-20 mb-28 overflow-hidden rounded-3xl shadow-md sm:block md:block lg:hidden xl:hidden 2xl:hidden mx-auto">
|
||||||
<img
|
<img
|
||||||
src={`http://localhost/${course.images[0].image}`}
|
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${course.images[0].image}`}
|
||||||
alt="Course Image"
|
alt="Course Image"
|
||||||
className="absolute inset-0 w-full h-full object-cover object-center "
|
className="absolute inset-0 w-full h-full object-cover object-center "
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: docker-next
|
||||||
|
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- '3000:3000'
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- /app/node_modules
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.docker.network=traefik-public
|
||||||
|
- traefik.constraint-label=traefik-public
|
||||||
|
|
||||||
|
- traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80
|
||||||
|
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.entrypoints=http
|
||||||
|
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.entrypoints=https
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls=true
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls.certresolver=le
|
||||||
|
|
||||||
|
# Enable www redirection for HTTP and HTTPS
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.middlewares=${STACK_NAME?Variable not set}-www-redirect
|
||||||
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.middlewares=https-redirect,${STACK_NAME?Variable not set}-www-redirect
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
- default
|
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
|
@ -11,7 +11,7 @@ export async function postMessage(data: MessageProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = JSON.stringify(data);
|
const body = JSON.stringify(data);
|
||||||
const url = `http://localhost/api/v1/messages/`;
|
const url = `${process.env.NEXT_PUBLIC_API_URL}messages/`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -41,7 +41,7 @@ export async function fetchAboutus() {
|
||||||
const headers: HeadersInit = {
|
const headers: HeadersInit = {
|
||||||
accept: "application/json"
|
accept: "application/json"
|
||||||
};
|
};
|
||||||
const url = `${process.env.api_url}aboutUs`;
|
const url = `${process.env.NEXT_PUBLIC_API_URL}aboutUs/`;
|
||||||
console.log('Fetching from URL:', url);
|
console.log('Fetching from URL:', url);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, { headers });
|
const response = await fetch(url, { headers });
|
||||||
|
@ -67,7 +67,7 @@ export async function fetchCourses() {
|
||||||
accept: "application/json"
|
accept: "application/json"
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = `${process.env.api_url}course/?skip=0&limit=100`;
|
const url = `${process.env.NEXT_PUBLIC_API_URL}course/?skip=0&limit=100`;
|
||||||
console.log('Fetching from URL:', url);
|
console.log('Fetching from URL:', url);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -83,7 +83,11 @@ export async function fetchCourses() {
|
||||||
info_images: course?.info_images?.sort((a: any, b: any) => a.index - b.index),
|
info_images: course?.info_images?.sort((a: any, b: any) => a.index - b.index),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return coursesArray;
|
const sortedCoursesArray = coursesArray.sort((a, b) =>
|
||||||
|
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortedCoursesArray;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fetch error:', error);
|
console.error('Fetch error:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -95,7 +99,7 @@ export async function fetchCourse(id: string) {
|
||||||
accept: "application/json"
|
accept: "application/json"
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = `${process.env.api_url}course/${id}`;
|
const url = `${process.env.NEXT_PUBLIC_API_URL}course/${id}/`;
|
||||||
console.log('Fetching from URL:', url);
|
console.log('Fetching from URL:', url);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -120,7 +124,7 @@ export async function fetchSettings() {
|
||||||
const headers: HeadersInit = {
|
const headers: HeadersInit = {
|
||||||
accept: "application/json"
|
accept: "application/json"
|
||||||
};
|
};
|
||||||
const url = `${process.env.api_url}setting`;
|
const url = `${process.env.NEXT_PUBLIC_API_URL}setting/`;
|
||||||
console.log('Fetching from URL:', url);
|
console.log('Fetching from URL:', url);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, { headers });
|
const response = await fetch(url, { headers });
|
||||||
|
|
Loading…
Reference in New Issue