diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b64b8684..810039aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Docker Login uses: docker/login-action@v3.1.0 @@ -29,20 +29,60 @@ jobs: context: ./web file: ./web/Dockerfile push: true - tags: ghcr.io/${{github.repository_owner}}/g-yachts-web:${{github.sha}} + tags: ghcr.io/${{github.repository_owner}}/g-yachts-web:${{github.sha} + + - name: Build and push API + uses: docker/build-push-action@v5.3.0 + with: + context: ./api + file: ./api/Dockerfile + push: true + tags: ghcr.io/${GITHUB_OWNER}/g-yachts-api:${GITHUB_SHA} Deploy: runs-on: ubuntu-latest needs: Build + + env: + GITHUB_OWNER: ${{github.repository_owner}} + GITHUB_SHA: ${{github.sha}} + CURRENCY_API_KEY: ${{secrets.CURRENCY_API}} + MONGO_URI: mongodb://${{secrets.SERVER_IP}}:27017 + MONGO_USER: ${{secrets.MONGO_USER}} + MONGO_PASS: ${{secrets.MONGO_PASS}} + SUBNET_IP: ${{secrets.SUBNET_WEB_IP}} + steps: - - name: Deploy + - name: Checkout + uses: actions/checkout@v4 + + - name: SSH + uses: appleboy/ssh-action@master + with: + host: ${{secrets.SERVER_IP}} + username: ${{secrets.SERVER_USER}} + password: ${{secrets.SERVER_PASSWORD}} + script: | + sudo mkdir -p /var/www/docker + cd /var/www/docker + + - name: Transfer Compose + uses: appleboy/scp-action@master + with: + host: ${{secrets.SERVER_IP}} + username: ${{secrets.SERVER_USER}} + password: ${{secrets.SERVER_PASSWORD}} + source: ./docker-compose.yml + target: /var/www/docker + + - name: Start Services uses: appleboy/ssh-action@master with: host: ${{secrets.SERVER_IP}} username: ${{secrets.SERVER_USER}} password: ${{secrets.SERVER_PASSWORD}} script: | - sudo docker pull ghcr.io/${{github.repository_owner}}/g-yachts-web:${{github.sha}} - sudo docker stop g-yachts-web || true - sudo docker rm g-yachts-web || true - sudo docker run -d --name g-yachts-web --network="host" -p 3000:3000 -e CURRENCY_API_KEY=${{secrets.CURRENCY_API}} -e MONGO_URI=mongodb://${{secrets.SERVER_IP}}:27017 -e MONGO_USER=${{secrets.MONGO_USER}} -e MONGO_PASS=${{secrets.MONGO_PASS}} ghcr.io/${{github.repository_owner}}/g-yachts-web:${{github.sha}} + cd /var/www/docker + docker-compose pull + docker-compose down + docker-compose up -d \ No newline at end of file diff --git a/admin/app/actions.ts b/admin/app/actions.ts new file mode 100644 index 00000000..128bbe5b --- /dev/null +++ b/admin/app/actions.ts @@ -0,0 +1,16 @@ +"use server"; + +export const uploadImages = async (formData: FormData, id: string) => { + const rawFormData = formData.getAll("images"); + const d = new FormData(); + rawFormData.forEach((f) => d.append("images", f)); + try { + const res = await fetch(`${process.env.API_URL}/yachts/images/${id}`, { + method: "POST", + body: d, + }); + return await res.json(); + } catch (e) { + console.error(e); + } +}; diff --git a/admin/app/globals.css b/admin/app/globals.css index 875c01e8..a90f0749 100644 --- a/admin/app/globals.css +++ b/admin/app/globals.css @@ -2,32 +2,3 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - -@layer utilities { - .text-balance { - text-wrap: balance; - } -} diff --git a/admin/app/layout.tsx b/admin/app/layout.tsx index 3314e478..95262c3a 100644 --- a/admin/app/layout.tsx +++ b/admin/app/layout.tsx @@ -1,11 +1,7 @@ import type { Metadata } from "next"; -import { Inter } from "next/font/google"; import "./globals.css"; - -const inter = Inter({ subsets: ["latin"] }); - export const metadata: Metadata = { - title: "Create Next App", + title: "Administration", description: "Generated by create next app", }; @@ -16,7 +12,7 @@ export default function RootLayout({ }>) { return ( - {children} + {children} ); } diff --git a/admin/app/page.tsx b/admin/app/page.tsx index 5705d4ea..36f887e8 100644 --- a/admin/app/page.tsx +++ b/admin/app/page.tsx @@ -1,113 +1,20 @@ -import Image from "next/image"; +"use client"; -export default function Home() { - return ( -
-
-

- Get started by editing  - app/page.tsx -

-
- - By{" "} - Vercel Logo - -
-
- -
- Next.js Logo -
- -
- -

- Docs{" "} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{" "} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
+import { uploadImages } from "@/app/actions"; - -

- Templates{" "} - - -> - -

-

- Explore starter templates for Next.js. -

-
- - -

- Deploy{" "} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
+const Home = () => { + return ( +
+
{ + await uploadImages(formData, "662810408f1b183f77e99b57"); + }} + > + + +
); -} +}; + +export default Home; diff --git a/admin/package-lock.json b/admin/package-lock.json index e6738c37..90ffe97e 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -8,7 +8,8 @@ "name": "admin", "version": "0.1.0", "dependencies": { - "next": "14.2.2", + "axios": "^1.6.8", + "next": "^14.2.3", "react": "^18", "react-dom": "^18" }, @@ -238,9 +239,9 @@ } }, "node_modules/@next/env": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.2.tgz", - "integrity": "sha512-sk72qRfM1Q90XZWYRoJKu/UWlTgihrASiYw/scb15u+tyzcze3bOuJ/UV6TBOQEeUaxOkRqGeuGUdiiuxc5oqw==" + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.2.2", @@ -252,9 +253,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.2.tgz", - "integrity": "sha512-3iPgMhzbalizGwHNFUcGnDhFPSgVBHQ8aqSTAMxB5BvJG0oYrDf1WOJZlbXBgunOEj/8KMVbejEur/FpvFsgFQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", "cpu": [ "arm64" ], @@ -267,9 +268,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.2.tgz", - "integrity": "sha512-x7Afi/jt0ZBRUZHTi49yyej4o8znfIMHO4RvThuoc0P+uli8Jd99y5GKjxoYunPKsXL09xBXEM1+OQy2xEL0Ag==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", "cpu": [ "x64" ], @@ -282,9 +283,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.2.tgz", - "integrity": "sha512-zbfPtkk7L41ODMJwSp5VbmPozPmMMQrzAc0HAUomVeVIIwlDGs/UCqLJvLNDt4jpWgc21SjjyIn762lNGrMaUA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", "cpu": [ "arm64" ], @@ -297,9 +298,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.2.tgz", - "integrity": "sha512-wPbS3pI/JU16rm3XdLvvTmlsmm1nd+sBa2ohXgBZcShX4TgOjD4R+RqHKlI1cjo/jDZKXt6OxmcU0Iys0OC/yg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", "cpu": [ "arm64" ], @@ -312,9 +313,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.2.tgz", - "integrity": "sha512-NqWOHqqq8iC9tuHvZxjQ2tX+jWy2X9y8NX2mcB4sj2bIccuCxbIZrU/ThFPZZPauygajZuVQ6zediejQHwZHwQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", "cpu": [ "x64" ], @@ -327,9 +328,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.2.tgz", - "integrity": "sha512-lGepHhwb9sGhCcU7999+iK1ZZT+6rrIoVg40MP7DZski9GIZP80wORSbt5kJzh9v2x2ev2lxC6VgwMQT0PcgTA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", "cpu": [ "x64" ], @@ -342,9 +343,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.2.tgz", - "integrity": "sha512-TZSh/48SfcLEQ4rD25VVn2kdIgUWmMflRX3OiyPwGNXn3NiyPqhqei/BaqCYXViIQ+6QsG9R0C8LftMqy8JPMA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", "cpu": [ "arm64" ], @@ -357,9 +358,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.2.tgz", - "integrity": "sha512-M0tBVNMEBJN2ZNQWlcekMn6pvLria7Sa2Fai5znm7CCJz4pP3lrvlSxhKdkCerk0D9E0bqx5yAo3o2Q7RrD4gA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", "cpu": [ "ia32" ], @@ -372,9 +373,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.2.tgz", - "integrity": "sha512-a/20E/wtTJZ3Ykv3f/8F0l7TtgQa2LWHU2oNB9bsu0VjqGuGGHmm/q6waoUNQYTVPYrrlxxaHjJcDV6aiSTt/w==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", "cpu": [ "x64" ], @@ -899,6 +900,11 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -923,6 +929,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1114,6 +1130,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1275,6 +1302,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2042,6 +2077,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2067,6 +2121,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3046,6 +3113,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3117,11 +3203,11 @@ "dev": true }, "node_modules/next": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.2.tgz", - "integrity": "sha512-oGwUaa2bCs47FbuxWMpOoXtBMPYpvTPgdZr3UAo+pu7Ns00z9otmYpoeV1HEiYL06AlRQQIA/ypK526KjJfaxg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", "dependencies": { - "@next/env": "14.2.2", + "@next/env": "14.2.3", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -3136,15 +3222,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.2", - "@next/swc-darwin-x64": "14.2.2", - "@next/swc-linux-arm64-gnu": "14.2.2", - "@next/swc-linux-arm64-musl": "14.2.2", - "@next/swc-linux-x64-gnu": "14.2.2", - "@next/swc-linux-x64-musl": "14.2.2", - "@next/swc-win32-arm64-msvc": "14.2.2", - "@next/swc-win32-ia32-msvc": "14.2.2", - "@next/swc-win32-x64-msvc": "14.2.2" + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -3674,6 +3760,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/admin/package.json b/admin/package.json index 67fb36a2..60feb707 100644 --- a/admin/package.json +++ b/admin/package.json @@ -3,24 +3,25 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev --turbo", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { + "axios": "^1.6.8", + "next": "^14.2.3", "react": "^18", - "react-dom": "^18", - "next": "14.2.2" + "react-dom": "^18" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.2", "postcss": "^8", "tailwindcss": "^3.4.1", - "eslint": "^8", - "eslint-config-next": "14.2.2" + "typescript": "^5" } } diff --git a/admin/tailwind.config.ts b/admin/tailwind.config.ts index 7e4bd91a..708743e6 100644 --- a/admin/tailwind.config.ts +++ b/admin/tailwind.config.ts @@ -6,15 +6,7 @@ const config: Config = { "./components/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", ], - theme: { - extend: { - backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", - }, - }, - }, + theme: {}, plugins: [], }; export default config; diff --git a/api/Dockerfile b/api/Dockerfile index 400eca1c..b6b5aa04 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,7 +1,7 @@ FROM node:20 LABEL authors="g-yachts" -WORKDIR /usr/src/api +WORKDIR /app/api COPY package*.json ./ @@ -9,10 +9,8 @@ RUN npm install COPY . . -ENV PORT=5001 - RUN npm run build -EXPOSE 5001 +EXPOSE 5000 CMD ["npm", "start"] diff --git a/api/package-lock.json b/api/package-lock.json index b76a6e51..2a9031dd 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -14,12 +14,14 @@ "dotenv": "^16.4.5", "express": "^4.19.1", "mongoose": "^8.3.2", + "multer": "^1.4.5-lts.1", "ts-node": "^10.9.2", "typescript": "^5.4.3" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", + "@types/multer": "^1.4.11", "nodemon": "^3.1.0" } }, @@ -148,6 +150,15 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/multer": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", + "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "20.11.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", @@ -252,6 +263,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -346,6 +362,22 @@ "node": ">=16.20.1" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -402,6 +434,20 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -434,6 +480,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -879,6 +930,11 @@ "node": ">=0.12.0" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -972,6 +1028,25 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mongodb": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.5.0.tgz", @@ -1076,6 +1151,23 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1188,6 +1280,11 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1250,6 +1347,25 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1427,6 +1543,27 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1536,6 +1673,11 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/typescript": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", @@ -1567,6 +1709,11 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1608,6 +1755,14 @@ "node": ">=16" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/api/package.json b/api/package.json index e1b5ee24..fdaaca52 100644 --- a/api/package.json +++ b/api/package.json @@ -17,12 +17,14 @@ "dotenv": "^16.4.5", "express": "^4.19.1", "mongoose": "^8.3.2", + "multer": "^1.4.5-lts.1", "ts-node": "^10.9.2", "typescript": "^5.4.3" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", + "@types/multer": "^1.4.11", "nodemon": "^3.1.0" } } diff --git a/api/src/controllers/customerControllers.ts b/api/src/controllers/customerControllers.ts deleted file mode 100644 index 2fbe5f38..00000000 --- a/api/src/controllers/customerControllers.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Customer } from "../models/customer"; -import { Contact } from "../models/contact"; -import { Request, Response } from "express"; - -export const newCustomer = async (req: Request, res: Response) => { - const { email, ...rest } = req.body; - try { - let customer = await Customer.findOne({ email }); - if (customer) { - customer = await Customer.findOneAndUpdate( - { email }, - { $set: rest }, - { new: true }, - ); - return res.status(202); - } - const archived = await Contact.findOneAndDelete({ email }); - if (archived) { - const createCustomer = new Customer(req.body); - await createCustomer.save(); - return res.status(200); - } - const createCustomer = new Customer(req.body); - await createCustomer.save(); - return res.status(201); - } catch (e) { - return res.status(500).send(e); - } -}; diff --git a/api/src/controllers/yachtsControllers.ts b/api/src/controllers/yachtsControllers.ts index 7bd6811e..a1375339 100644 --- a/api/src/controllers/yachtsControllers.ts +++ b/api/src/controllers/yachtsControllers.ts @@ -1,14 +1,30 @@ -import { Featured } from "../models/yachts"; import { Request, Response } from "express"; +import path from "path"; +import fs from "fs"; +import multer from "multer"; -export const getFeatured = async (req: Request, res: Response) => { +export const getImages = async (req: Request, res: Response) => { try { - console.log("getFeatured called"); - const d = await Featured.find({ featured: true }).select( - "name price builder length yearBuilt sleeps", - ); - res.json(d); + const { id } = req.params; + const dir = path.join("/app/api/images/yachts", id); + fs.readdir(dir, (e, f) => { + const d = f.map((f) => `http://51.75.16.185/images/${id}/${f}`); + res.json(d); + }); } catch (e) { res.status(500).send(e); } }; + +const storage = multer.diskStorage({ + destination: (req, f, cb) => { + const { id } = req.params; + const dir = path.join("/app/api/images/yachts", id); + fs.mkdirSync(dir, { recursive: true }); + cb(null, dir); + }, + filename: (req, f, cb) => { + cb(null, f.originalname); + }, +}); +export const uploadImages = multer({ storage }); diff --git a/api/src/index.ts b/api/src/index.ts index 55b3f5e7..98050043 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -1,54 +1,41 @@ -import mongoose from "mongoose"; import express from "express"; -import yachtsRoutes from "./routes/yachtsRoutes"; -//import customerRoutes from "./routes/customerRoutes"; import dotenv from "dotenv"; import cors from "cors"; -import { web } from "./mongoose.web"; -import { panel } from "./mongoose.panel"; +import adminRoutes from "./routes/adminRoutes"; +import publicRoutes from "./routes/publicRoutes"; dotenv.config(); -const app = express(); -const PORT = process.env.PORT || 3000; -const uri = `mongodb://${process.env.MONGO_HOST}:27017`, - user = process.env.MONGO_USER || "", - pass = process.env.MONGO_PASS || ""; - -web.then(() => { - app.use("/yachts", yachtsRoutes); -}); - -panel.on("connected", () => { - console.log("MongoDB panel connected"); -}); - -/*mongoose - .connect(uri, { - dbName: db, - user: user, - pass: pass, - }) - .then(() => console.log("MongoDB connected")) - .catch((e) => console.log(e));*/ - -app.use(express.json()); -app.use( - cors({ - origin: process.env.WEB_URL, - methods: "GET,HEAD,PUT,PATCH,POST,DELETE", - allowedHeaders: "Content-Type, Authorization", - credentials: true, - }), -); - -/*const openRoutes = async () => { - web.then(() => { - app.use("/yachts", yachtsRoutes); - }); -};*/ -//app.use("/customer", customerRoutes); - -app.listen(PORT, () => { - console.log(`Server is at port ${PORT}.`); -}); +const port = process.env.PORT || 5000; +const env = process.env.NODE_ENV || "public"; +const host = process.env.HOST || "localhost"; +const app = express().use(express.json()); + +switch (env) { + case "public": + app + .use( + cors({ + origin: `http://${host}:3000`, + methods: "GET,HEAD,PUT,PATCH,POST", + allowedHeaders: "Content-Type, Authorization", + credentials: true, + }), + ) + .use("/", publicRoutes) + .listen(port, () => console.log(`Public server is at port ${port}.`)); + break; + case "admin": + app + .use( + cors({ + origin: `http://${host}:3000`, + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", + allowedHeaders: "Content-Type, Authorization", + credentials: true, + }), + ) + .use("/", adminRoutes) + .use("/", publicRoutes) + .listen(port, () => console.log(`Admin server is at port ${port}.`)); +} diff --git a/api/src/models/contact.ts b/api/src/models/contact.ts deleted file mode 100644 index e2e94b3b..00000000 --- a/api/src/models/contact.ts +++ /dev/null @@ -1,19 +0,0 @@ -import mongoose, { Schema } from "mongoose"; -import { IContact } from "../types/customer"; - -const ContactSchema: Schema = new Schema( - { - name: { type: String, required: true }, - email: { type: String, required: true }, - tel: { type: String, required: false }, - newsletter: { type: Boolean, required: false }, - }, - { - collection: "archived customers", - }, -); - -const Contact = - mongoose.models.Contact || mongoose.model("Contact", ContactSchema); - -export { Contact, type IContact }; diff --git a/api/src/models/customer.ts b/api/src/models/customer.ts deleted file mode 100644 index 29552750..00000000 --- a/api/src/models/customer.ts +++ /dev/null @@ -1,26 +0,0 @@ -import mongoose, { Schema } from "mongoose"; -import { ICustomer } from "../types/customer"; - -const CustomerSchema: Schema = new Schema( - { - name: { type: String, required: true }, - email: { type: String, required: true }, - tel: { type: String, required: false }, - message: { type: String, required: false }, - inquiry: { - buying: { type: Boolean, required: false }, - selling: { type: Boolean, required: false }, - chartering: { type: Boolean, required: false }, - other: { type: Boolean, required: false }, - }, - newsletter: { type: Boolean, required: false }, - }, - { - collection: "customers", - }, -); - -const Customer = - mongoose.models.Customer || mongoose.model("Customer", CustomerSchema); - -export { Customer, type ICustomer }; diff --git a/api/src/models/yachts.ts b/api/src/models/yachts.ts deleted file mode 100644 index acb0ee73..00000000 --- a/api/src/models/yachts.ts +++ /dev/null @@ -1,21 +0,0 @@ -import mongoose, { Schema } from "mongoose"; -import { IFeatured } from "../types/yachts"; - -const FeaturedSchema: Schema = new Schema( - { - name: { type: String, required: true }, - price: { type: Number, required: true }, - builder: { type: String, required: true }, - length: { type: Number, required: true }, - yearBuilt: { type: Number, required: true }, - sleeps: { type: Number, required: true }, - }, - { - collection: "yachts", - }, -); - -const Featured = - mongoose.models.Featured || mongoose.model("Featured", FeaturedSchema); - -export { Featured, type IFeatured }; diff --git a/api/src/mongoose.panel.ts b/api/src/mongoose.panel.ts deleted file mode 100644 index 840decd1..00000000 --- a/api/src/mongoose.panel.ts +++ /dev/null @@ -1,11 +0,0 @@ -import mongoose from "mongoose"; - -const uri = `mongodb://${process.env.MONGO_HOST}:27017`, - user = process.env.MONGO_USER || "", - pass = process.env.MONGO_PASS || ""; - -export const panel = mongoose.createConnection(uri, { - dbName: "panel", - user: user, - pass: pass, -}); diff --git a/api/src/mongoose.web.ts b/api/src/mongoose.web.ts deleted file mode 100644 index 33114627..00000000 --- a/api/src/mongoose.web.ts +++ /dev/null @@ -1,14 +0,0 @@ -import mongoose from "mongoose"; - -const uri = `mongodb://${process.env.MONGO_HOST}:27017`, - user = process.env.MONGO_USER || "", - pass = process.env.MONGO_PASS || ""; - -export const web = mongoose - .createConnection(uri, { - dbName: "web", - user: user, - pass: pass, - }) - .asPromise() - .then(() => console.log("Connection established")); diff --git a/api/src/routes/adminRoutes.ts b/api/src/routes/adminRoutes.ts new file mode 100644 index 00000000..cb12794b --- /dev/null +++ b/api/src/routes/adminRoutes.ts @@ -0,0 +1,10 @@ +import express from "express"; +import { uploadImages } from "../controllers/yachtsControllers"; + +const router = express.Router(); + +router.post("/yachts/images/:id", uploadImages.array("images"), (req, res) => + res.status(200), +); + +export default router; diff --git a/api/src/routes/customerRoutes.ts b/api/src/routes/customerRoutes.ts deleted file mode 100644 index eaffdba3..00000000 --- a/api/src/routes/customerRoutes.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from "express"; -import * as customerControllers from "../controllers/customerControllers"; - -const router = express.Router(); - -router.get("/new", customerControllers.newCustomer); - -export default router; diff --git a/api/src/routes/publicRoutes.ts b/api/src/routes/publicRoutes.ts new file mode 100644 index 00000000..af833f94 --- /dev/null +++ b/api/src/routes/publicRoutes.ts @@ -0,0 +1,8 @@ +import express from "express"; +import { getImages } from "../controllers/yachtsControllers"; + +const router = express.Router(); + +router.get("/yachts/images/:id", getImages); + +export default router; diff --git a/api/src/routes/yachtsRoutes.ts b/api/src/routes/yachtsRoutes.ts index d7773b09..6dbf4777 100644 --- a/api/src/routes/yachtsRoutes.ts +++ b/api/src/routes/yachtsRoutes.ts @@ -3,6 +3,11 @@ import * as yachtsControllers from "../controllers/yachtsControllers"; const router = express.Router(); -router.get("/featured", yachtsControllers.getFeatured); +router.get("/images/:id", yachtsControllers.getImages); +router.post( + "/images/:id", + yachtsControllers.uploadImages.array("images"), + (req, res) => res.status(200), +); export default router; diff --git a/api/src/types/customer.d.ts b/api/src/types/customer.d.ts deleted file mode 100644 index bc925dc3..00000000 --- a/api/src/types/customer.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface IContact { - name: string; - email: string; - tel?: string; - newsletter?: boolean; -} - -export interface ICustomer extends IContact { - message?: string; - inquiry?: { - buying: boolean; - selling: boolean; - chartering: boolean; - other: boolean; - }; -} diff --git a/api/src/types/yachts.d.ts b/api/src/types/yachts.d.ts deleted file mode 100644 index 894f896c..00000000 --- a/api/src/types/yachts.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Document } from "mongoose"; - -export interface IFeatured extends Document { - name: string; - price: number; - builder: string; - length: number; - yearBuilt: number; - sleeps: number; -} diff --git a/api/src/utils/mongoose.ts b/api/src/utils/mongoose.ts new file mode 100644 index 00000000..b9792dd7 --- /dev/null +++ b/api/src/utils/mongoose.ts @@ -0,0 +1,13 @@ +import mongoose from "mongoose"; + +export const useWeb = mongoose.createConnection(process.env.MONGO_URI!, { + dbName: "web", + user: process.env.MONGO_USER, + pass: process.env.MONGO_PASS, +}); + +export const usePanel = mongoose.createConnection(process.env.MONGO_URI!, { + dbName: "panel", + user: process.env.MONGO_USER, + pass: process.env.MONGO_PASS, +}); diff --git a/docker-compose.yml b/docker-compose.yml index e2b94258..484032c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,19 +1,54 @@ version: '3.8' services: - web: - build: - context: . - dockerfile: web/Dockerfile + nginx: + image: nginx:latest ports: - - "3000:3000" + - "80:80" volumes: - - ./web:/usr/src/web + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + networks: + - public + + web: + image: ghcr.io/${GITHUB_OWNER}/g-yachts-web:${GITHUB_SHA} + environment: + - CURRENCY_API_KEY=${CURRENCY_API_KEY} + - MONGO_URI=${MONGO_URI} + - MONGO_USER=${MONGO_USER} + - MONGO_PASS=${MONGO_PASS} + ports: + - "3000:3000" + networks: + public: + ipv4_address: ${SUBNET_IP} - api: - build: - context: . - dockerfile: api/Dockerfile + api-public: + image: ghcr.io/${GITHUB_OWNER}/g-yachts-api:${GITHUB_SHA} + environment: + - NODE_ENV=public + - PORT=5000 + - HOST=${SUBNET_IP} ports: - - "5001:5001" + - "5000:5000" + networks: + public: + ipv4_address: ${SUBNET_IP} volumes: - - ./api:/usr/src/api \ No newline at end of file + - /app/api/images/yachts:/var/www/images/yachts + + api-admin: + image: ghcr.io/${GITHUB_OWNER}/g-yachts-api:${GITHUB_SHA} + environment: + - NODE_ENV=admin + - PORT=5000 + - HOST=localhost + network_mode: host + volumes: + - /app/api/images/yachts:/var/www/images/yachts + +networks: + public: + ipam: + driver: default + config: + - subnet: "192.168.92.0/24" diff --git a/web/Dockerfile b/web/Dockerfile index 7781d1a0..c7665292 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,7 +1,7 @@ FROM node:20 LABEL authors="g-yachts" -WORKDIR /usr/src/web +WORKDIR /app/web COPY package*.json ./ diff --git a/web/app/[locale]/page.tsx b/web/app/[locale]/page.tsx index d9018fa5..0fea00c6 100644 --- a/web/app/[locale]/page.tsx +++ b/web/app/[locale]/page.tsx @@ -1,15 +1,16 @@ -import Bar from "@/components/nav/section"; +import Bar from "@/components/nav/bar"; import Hero from "@/components/index/hero"; import { useTranslations } from "next-intl"; import dynamic from "next/dynamic"; import { Link } from "@/navigation"; +const View = dynamic(() => import("@/components/nav/view")); const Services = dynamic(() => import("@/components/index/services")); const WorkingTogether = dynamic(() => import("@/components/index/working")); const Featured = dynamic(() => import("@/components/index/featured/section")); const Memories = dynamic(() => import("@/components/index/memories")); const Newsletter = dynamic(() => import("@/components/newsletter")); -const Footer = dynamic(() => import("@/components/footer/section")); +const Footer = dynamic(() => import("@/components/footer")); const Home = () => { const t = useTranslations("index"); @@ -17,6 +18,7 @@ const Home = () => { return (
+
diff --git a/web/app/actions.ts b/web/app/actions.ts index b62ad0fd..c927aff0 100644 --- a/web/app/actions.ts +++ b/web/app/actions.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { formatCurrency } from "@/utils/yachts"; -import { Featured } from "@/models/yachts"; +import { Yacht } from "@/models/yacht"; import { Customer } from "@/models/customer"; import { Newsletter } from "@/models/newsletter"; @@ -73,7 +73,7 @@ export const contact = async (formData: FormData) => { }; export const fetchFeatured = async () => { - return await Featured.find({ featured: true }) + return await Yacht.find({ featured: true }) .select("name price builder length yearBuilt sleeps") .catch((e) => { throw e; diff --git a/web/app/store.ts b/web/app/store.ts new file mode 100644 index 00000000..a57a8cac --- /dev/null +++ b/web/app/store.ts @@ -0,0 +1,14 @@ +import { create } from "zustand"; +import { IView, IViewActions } from "@/types/view"; +import { currency } from "@/utils/yachts"; +import Cookies from "js-cookie"; + +export const useView = create()((set) => ({ + currency: currency(), + view: null, + setCurrency: (code) => { + Cookies.set("currency", code); + set({ currency: code }); + }, + openView: (view) => set({ view }), +})); diff --git a/web/components/footer/components.tsx b/web/components/footer.tsx similarity index 89% rename from web/components/footer/components.tsx rename to web/components/footer.tsx index 6d7ff95d..9b1ff3c2 100644 --- a/web/components/footer/components.tsx +++ b/web/components/footer.tsx @@ -1,12 +1,14 @@ +"use client"; + import Logo from "@/public/logo/logo"; import SocialLinks from "@/components/nav/social"; import { Link } from "@/navigation"; import { useTranslations } from "next-intl"; -import { useInteraction } from "@/contexts/interact"; +import { useView } from "@/app/store"; -const Components = () => { +const Footer = () => { const t = useTranslations(), - { openUI } = useInteraction(); + { openView } = useView(); return (
{ {t("navigation.links.company")}
- -
- {carouselData.map((card, i) => ( - - ))} -
-
- {carouselExtended.map((card, i) => ( - - ))} -
-
+
+ {carouselData.map((card, i) => ( + + ))} +
+
+ {carouselExtended.map((card, i) => ( + + ))} +
); }; diff --git a/web/components/nav/bar.tsx b/web/components/nav/bar.tsx new file mode 100644 index 00000000..a08e3e37 --- /dev/null +++ b/web/components/nav/bar.tsx @@ -0,0 +1,79 @@ +"use client"; + +import View from "@/components/nav/view"; +import Logo from "@/public/logo/logo"; +import { useTranslations } from "next-intl"; +import { useEffect, useState } from "react"; +import { useView } from "@/app/store"; + +const Bar = ({ dynamicColor }: { dynamicColor: number }) => { + const t = useTranslations("bar"), + { openView } = useView(), + [isScrolled, setScrolled] = useState(false); + + useEffect(() => { + const handleScroll = () => { + const position = window.scrollY; + const threshold = dynamicColor; + if (position > threshold) setScrolled(true); + else if (position <= threshold) setScrolled(false); + }; + + window.addEventListener("scroll", handleScroll); + + return () => window.removeEventListener("scroll", handleScroll); + }, [isScrolled]); + + return ( + <> +
+ +
+ +
+ +
+
+ + ); +}; + +export default Bar; diff --git a/web/components/nav/burger.tsx b/web/components/nav/burger.tsx deleted file mode 100644 index c9dc191f..00000000 --- a/web/components/nav/burger.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import dynamic from "next/dynamic"; -import { motion, AnimatePresence } from "framer-motion"; -import { useInteraction } from "@/contexts/interact"; - -const Navigation: React.ComponentType = dynamic( - () => import("@/components/nav/navigation"), -); -const Contact: React.ComponentType = dynamic( - () => import("@/components/nav/contact"), -); - -const Burger = ({ - opened, - setOpened, - dark, -}: { - opened: "navigation" | "contact" | null; - setOpened: (opened: "navigation" | "contact" | null) => void; - dark: boolean; -}) => { - const { openUI } = useInteraction(); - - return ( - <> - - {opened && ( - - - - ); -}; - -export default Burger; diff --git a/web/components/nav/components.tsx b/web/components/nav/components.tsx deleted file mode 100644 index ba1edbae..00000000 --- a/web/components/nav/components.tsx +++ /dev/null @@ -1,54 +0,0 @@ -"use client"; - -import Burger from "@/components/nav/burger"; -import Logo from "@/public/logo/logo"; -import { useTranslations } from "next-intl"; -import { useEffect, useState } from "react"; -import { useInteraction } from "@/contexts/interact"; - -const Components = ({ dynamicColor }: { dynamicColor: number }) => { - const t = useTranslations("bar"), - { UI, openUI } = useInteraction(), - [isScrolled, setScrolled] = useState(false); - - useEffect(() => { - const handleScroll = () => { - const position = window.scrollY; - const threshold = dynamicColor; - if (position > threshold) setScrolled(true); - else if (position <= threshold) setScrolled(false); - }; - - window.addEventListener("scroll", handleScroll); - - return () => window.removeEventListener("scroll", handleScroll); - }, [isScrolled]); - - return ( - <> -
-
- - -
- -
-
- - ); -}; - -export default Components; diff --git a/web/components/nav/navigation.tsx b/web/components/nav/navigation.tsx index 11d7412e..34cefcd2 100644 --- a/web/components/nav/navigation.tsx +++ b/web/components/nav/navigation.tsx @@ -4,15 +4,15 @@ import { usePathname, useRouter, Link, pathnames } from "@/navigation"; import Logo from "@/public/logo/logo"; import { motion } from "framer-motion"; import SocialLinks from "@/components/nav/social"; -import { useInteraction } from "@/contexts/interact"; import Whisper from "@/components/whisper"; import { handleMouseMove } from "@/utils/mouseCoords"; +import { useView } from "@/app/store"; export const Close = () => { - const { openUI } = useInteraction(); + const { openView } = useView(); return ( -
@@ -57,7 +57,7 @@ const Newsletter = () => { animate={{ opacity: 1 }} transition={{ duration: 0.5, ease: "easeInOut" }} className={ - "h-full w-max flex flex-col justify-center items-center lg:px-[4vh] px-[4vw] lg:py-[4vh] py-[4vw] text-white gap-[2vh]" + "h-full w-max flex flex-col justify-center items-center lg:px-[4vh] lg:py-[4vh] py-[4vw] text-white gap-[2vh]" } >

{t("registered")}

@@ -92,7 +92,7 @@ const Newsletter = () => { await contact(formData).then(() => setRegistred(true)); }} className={ - "h-full w-max bg-white flex flex-col justify-start items-center lg:px-[4vh] px-[4vw] lg:py-[4vh] py-[4vw] gap-[3vh]" + "h-full lg:w-max w-[92vw] bg-white flex flex-col justify-start items-center lg:px-[4vh] px-[4vw] lg:py-[4vh] py-[4vw] gap-[3vh]" } > { return (