From e4c7d72d7e327c311e3d10bc010c2f977fd52302 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 02:19:42 -0700 Subject: [PATCH 01/23] fix: Github Actions error fixed --- {backend/.github => .github}/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename {backend/.github => .github}/workflows/ci.yml (77%) diff --git a/backend/.github/workflows/ci.yml b/.github/workflows/ci.yml similarity index 77% rename from backend/.github/workflows/ci.yml rename to .github/workflows/ci.yml index 433c1cb..4fe2ac2 100644 --- a/backend/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v2 - name: Install dependencies - run: npm install + run: cd backend && npm install - name: Test - run: npm run test + run: cd backend && npm run test From 4afa61aefaa710ad44242b05825d2226dc2d53ce Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 02:29:02 -0700 Subject: [PATCH 02/23] Continuous integration badge added --- .github/workflows/ci.yml | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fe2ac2..31b7f77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,8 @@ jobs: testing: runs-on: ubuntu-latest + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} steps: - name: Checkout uses: actions/checkout@v2 diff --git a/README.md b/README.md index cfcc9e6..7057397 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # ♻️ Fashion +[![Continuous Integration](https://github.com/techstartucalgary/fashion/actions/workflows/ci.yml/badge.svg)](https://github.com/techstartucalgary/fashion/actions/workflows/ci.yml) + ## πŸ“– Table of Contents - [πŸ“ Contributors](#-contributors) From 0664f6b7fea134bececa096ef3bed13cdc753803 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 02:56:29 -0700 Subject: [PATCH 03/23] fix: Continuous integration improvements --- .github/actions/docker-compose/action.yml | 11 +++++++++++ .github/workflows/ci.yml | 7 +++++-- docker-compose.yml | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .github/actions/docker-compose/action.yml create mode 100644 docker-compose.yml diff --git a/.github/actions/docker-compose/action.yml b/.github/actions/docker-compose/action.yml new file mode 100644 index 0000000..a5c7dec --- /dev/null +++ b/.github/actions/docker-compose/action.yml @@ -0,0 +1,11 @@ +name: 'Docker-Compose Setup' +description: 'Sets up docker-compose' +runs: + using: 'composite' + steps: + - name: Download Docker-Compose plugin + shell: bash + run: curl -SL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose + - name: Make plugin executable + shell: bash + run: sudo chmod +x /usr/local/bin/docker-compose \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31b7f77..09b2480 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,13 +11,16 @@ jobs: testing: runs-on: ubuntu-latest env: - DATABASE_URL: ${{ secrets.DATABASE_URL }} + DATABASE_URL: mysql://root:root@localhost:3306/techstart-fashion steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install dependencies run: cd backend && npm install + - name: Start services + uses: ./.github/actions/docker-compose + - name: Test run: cd backend && npm run test diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..71b453f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3.8" +services: + db: + image: mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: techstart-fashion + ports: + - 3306:3306 + volumes: + - db:/var/lib/mysql + +volumes: + db: + driver: local From 34bea91d50b3ea6b138eb253538fdd4c55b7c283 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 03:02:49 -0700 Subject: [PATCH 04/23] fix: Continuous integration improvements --- .github/actions/docker-compose/action.yml | 11 ----------- .github/workflows/ci.yml | 14 ++++++++++++-- docker-compose.yml | 16 ---------------- 3 files changed, 12 insertions(+), 29 deletions(-) delete mode 100644 .github/actions/docker-compose/action.yml delete mode 100644 docker-compose.yml diff --git a/.github/actions/docker-compose/action.yml b/.github/actions/docker-compose/action.yml deleted file mode 100644 index a5c7dec..0000000 --- a/.github/actions/docker-compose/action.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: 'Docker-Compose Setup' -description: 'Sets up docker-compose' -runs: - using: 'composite' - steps: - - name: Download Docker-Compose plugin - shell: bash - run: curl -SL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose - - name: Make plugin executable - shell: bash - run: sudo chmod +x /usr/local/bin/docker-compose \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09b2480..4b50f74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,8 +19,18 @@ jobs: - name: Install dependencies run: cd backend && npm install - - name: Start services - uses: ./.github/actions/docker-compose + - name: Start volume + run: docker volume create db + + - name: Start database + run: docker run -d \ + --name db \ + -e MYSQL_ROOT_PASSWORD=root \ + -e MYSQL_DATABASE=techstart-fashion \ + -p 3306:3306 \ + -v db:/var/lib/mysql \ + --restart always \ + mysql - name: Test run: cd backend && npm run test diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 71b453f..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: "3.8" -services: - db: - image: mysql - restart: always - environment: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: techstart-fashion - ports: - - 3306:3306 - volumes: - - db:/var/lib/mysql - -volumes: - db: - driver: local From 5b2b3b0a49126bdc009d082a614fdae7835f0f3a Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 03:05:05 -0700 Subject: [PATCH 05/23] fix: Continuous integration improvements --- .github/workflows/ci.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b50f74..9f8190c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,14 +23,15 @@ jobs: run: docker volume create db - name: Start database - run: docker run -d \ - --name db \ - -e MYSQL_ROOT_PASSWORD=root \ - -e MYSQL_DATABASE=techstart-fashion \ - -p 3306:3306 \ - -v db:/var/lib/mysql \ - --restart always \ - mysql + run: | + docker run -d \ + --name db \ + -e MYSQL_ROOT_PASSWORD=root \ + -e MYSQL_DATABASE=techstart-fashion \ + -p 3306:3306 \ + -v db:/var/lib/mysql \ + --restart always \ + mysql - name: Test run: cd backend && npm run test From 86c1557a2c6be1f7525df4d9f9d73acd4f6daca6 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 03:22:14 -0700 Subject: [PATCH 06/23] fix: Continuous integration improvements --- backend/package.json | 2 +- backend/src/index.ts | 3 --- backend/src/routers/authentication.router.ts | 4 +++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 3643095..7aa3dfc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,7 +10,7 @@ "start": "NODE_ENV=PROD node dist/src/index.js", "predev": "npx prisma migrate dev --name init && npx prisma generate", "dev": "NODE_ENV=DEV nodemon -e ts --exec \"npm run build && node dist/src/index.js\"", - "pretest": "npx prisma migrate deploy && npx prisma generate", + "pretest": "npx prisma migrate dev --name init && npx prisma generate", "test": "NODE_ENV=TEST npm run build && mocha \"dist/test/**/*.test.js\"" }, "keywords": [], diff --git a/backend/src/index.ts b/backend/src/index.ts index 6e28f84..5d26cca 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -4,12 +4,9 @@ import algorithmRouter from "./routers/algorithm.router.js"; import authenticationRouter from "./routers/authentication.router.js"; import errorHandler from "./middlewares/error.middleware.js"; import { PORT } from "./config/config.js"; -import { PrismaClient } from "@prisma/client"; export const app: Express = express(); -export const prisma = new PrismaClient(); - app.use(cors()); app.use(express.json()); diff --git a/backend/src/routers/authentication.router.ts b/backend/src/routers/authentication.router.ts index 3d7f66d..7f074fc 100644 --- a/backend/src/routers/authentication.router.ts +++ b/backend/src/routers/authentication.router.ts @@ -1,5 +1,5 @@ import { Router } from "express"; -import { prisma } from "../index.js"; +import { PrismaClient } from "@prisma/client"; import UserRepository from "../repositories/user.repository.js"; import AuthenticationService from "../services/authentication.service.js"; import AuthenticationController from "../controllers/authentication.controller.js"; @@ -11,6 +11,8 @@ import { const authenticationRouter = Router(); +const prisma = new PrismaClient(); + const userRepository: UserRepositoryInterface = new UserRepository(prisma); const authenticationService: AuthenticationServiceInterface = From 9c928c7a8b749418a7111009674460382ab53dd1 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 03:25:11 -0700 Subject: [PATCH 07/23] fix: Continuous integration improvements --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f8190c..50d3e31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,5 +33,8 @@ jobs: --restart always \ mysql + - name: Wait for database + run: docker exec db mysqladmin --silent --wait=30 -uroot -proot ping || exit 1 + - name: Test run: cd backend && npm run test From 2472e592a5498234a979f23ccffe8619bad3e167 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 03:32:47 -0700 Subject: [PATCH 08/23] fix: Continuous integration improvements --- .github/workflows/ci.yml | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50d3e31..c43dc1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,11 +7,25 @@ on: - dev jobs: - testing: runs-on: ubuntu-latest + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: techstart-fashion + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping -h localhost -uroot -proot" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + env: DATABASE_URL: mysql://root:root@localhost:3306/techstart-fashion + steps: - name: Checkout uses: actions/checkout@v3 @@ -19,22 +33,13 @@ jobs: - name: Install dependencies run: cd backend && npm install - - name: Start volume - run: docker volume create db - - - name: Start database - run: | - docker run -d \ - --name db \ - -e MYSQL_ROOT_PASSWORD=root \ - -e MYSQL_DATABASE=techstart-fashion \ - -p 3306:3306 \ - -v db:/var/lib/mysql \ - --restart always \ - mysql - - name: Wait for database - run: docker exec db mysqladmin --silent --wait=30 -uroot -proot ping || exit 1 + run: | + until nc -z localhost 3306; do + echo "Waiting for MySQL to be ready..." + sleep 2 + done + echo "MySQL is ready." - name: Test run: cd backend && npm run test From c9f0789bd6941a429d61dc8fa94be443f3db2690 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 19 Nov 2023 23:22:00 -0700 Subject: [PATCH 09/23] feat: Session middleware with redis --- backend/docker-compose.yml | 20 +++ backend/package-lock.json | 164 +++++++++++++++++- backend/package.json | 6 +- backend/src/config/config.ts | 2 + backend/src/index.ts | 11 +- backend/src/middlewares/session.middleware.ts | 19 ++ 6 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 backend/docker-compose.yml create mode 100644 backend/src/middlewares/session.middleware.ts diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml new file mode 100644 index 0000000..afab97b --- /dev/null +++ b/backend/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3" + +services: + mysql: + image: mysql + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + ports: + - 3306:3306 + volumes: + - mysql:/var/lib/mysql + + redis: + image: redis + ports: + - 6379:6379 + +volumes: + mysql: diff --git a/backend/package-lock.json b/backend/package-lock.json index 354d682..3d0a191 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,14 +10,18 @@ "license": "ISC", "dependencies": { "@prisma/client": "^5.6.0", + "connect-redis": "^7.1.0", "cors": "^2.8.5", "dotenv": "^16.3.1", - "express": "^4.18.2" + "express": "^4.18.2", + "express-session": "^1.17.3", + "redis": "^4.6.10" }, "devDependencies": { "@types/chai": "^4.3.10", "@types/cors": "^2.8.16", "@types/express": "^4.17.21", + "@types/express-session": "^1.17.10", "@types/mocha": "^10.0.4", "@types/node": "^20.9.0", "@types/supertest": "^2.0.16", @@ -61,6 +65,59 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz", "integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==" }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.11.tgz", + "integrity": "sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.5.tgz", + "integrity": "sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -125,6 +182,15 @@ "@types/send": "*" } }, + "node_modules/@types/express-session": { + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.10.tgz", + "integrity": "sha512-U32bC/s0ejXijw5MAzyaV4tuZopCh/K7fPoUDyNbsRXHvPSeymygYD1RFL99YOLhF5PNOkzswvOTRaVHdL1zMw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -504,6 +570,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -546,6 +620,17 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/connect-redis": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.0.tgz", + "integrity": "sha512-UaqO1EirWjON2ENsyau7N5lbkrdYBpS6mYlXSeff/OYXsd6EGZ+SXSmNPoljL2PSua8fgjAEaldSA73PMZQ9Eg==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "express-session": ">=1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -791,6 +876,32 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "dependencies": { + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -924,6 +1035,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1557,6 +1676,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1696,6 +1823,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1739,6 +1874,19 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "4.6.10", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.10.tgz", + "integrity": "sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg==", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.11", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.6", + "@redis/search": "1.1.5", + "@redis/time-series": "1.0.5" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2075,6 +2223,17 @@ "node": ">=14.17" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -2152,8 +2311,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "16.2.0", diff --git a/backend/package.json b/backend/package.json index 7aa3dfc..9f49cff 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,14 +18,18 @@ "license": "ISC", "dependencies": { "@prisma/client": "^5.6.0", + "connect-redis": "^7.1.0", "cors": "^2.8.5", "dotenv": "^16.3.1", - "express": "^4.18.2" + "express": "^4.18.2", + "express-session": "^1.17.3", + "redis": "^4.6.10" }, "devDependencies": { "@types/chai": "^4.3.10", "@types/cors": "^2.8.16", "@types/express": "^4.17.21", + "@types/express-session": "^1.17.10", "@types/mocha": "^10.0.4", "@types/node": "^20.9.0", "@types/supertest": "^2.0.16", diff --git a/backend/src/config/config.ts b/backend/src/config/config.ts index c14de29..8afa7c2 100644 --- a/backend/src/config/config.ts +++ b/backend/src/config/config.ts @@ -2,3 +2,5 @@ import dotenv from "dotenv"; dotenv.config(); export const PORT: number = Number(process.env.PORT) || 8080; +export const SESSION_SECRET: string = process.env.SESSION_SECRET || "secret"; +export const REDIS_URL: string = process.env.REDIS_URL || "redis"; diff --git a/backend/src/index.ts b/backend/src/index.ts index 5d26cca..8b12ff8 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,15 +1,20 @@ import express, { Express } from "express"; import cors from "cors"; +import { PORT } from "./config/config.js"; + +import session from "./middlewares/session.middleware.js"; +import errorHandler from "./middlewares/error.middleware.js"; + import algorithmRouter from "./routers/algorithm.router.js"; import authenticationRouter from "./routers/authentication.router.js"; -import errorHandler from "./middlewares/error.middleware.js"; -import { PORT } from "./config/config.js"; export const app: Express = express(); +app.use(express.json()); + app.use(cors()); -app.use(express.json()); +app.use(session); app.use("/auth", authenticationRouter); diff --git a/backend/src/middlewares/session.middleware.ts b/backend/src/middlewares/session.middleware.ts new file mode 100644 index 0000000..e9206f9 --- /dev/null +++ b/backend/src/middlewares/session.middleware.ts @@ -0,0 +1,19 @@ +import session from "express-session"; +import redis from "redis"; +import { SESSION_SECRET, REDIS_URL } from "../config/config"; +const RedisStore = require("connect-redis")(session); + +const redisClient = redis.createClient({ + url: REDIS_URL, +}); + +export default session({ + store: new RedisStore({ client: redisClient }), + secret: SESSION_SECRET, + cookie: { + secure: process.env.NODE_ENV === "PROD", + sameSite: process.env.NODE_ENV === "PROD" ? "none" : "lax", + maxAge: + process.env.NODE_ENV === "TEST" ? 1000 * 60 * 5 : 1000 * 60 * 60 * 24 * 7, + }, +}); From 7bdc628bcb9df00c4cb2af93b65f8e59fe7d67e4 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Thu, 23 Nov 2023 18:28:19 -0700 Subject: [PATCH 10/23] docs: Documentation for installation updated --- README.md | 63 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 7057397..1a3e46b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ♻️ Fashion +# ♻️ Fashion App [![Continuous Integration](https://github.com/techstartucalgary/fashion/actions/workflows/ci.yml/badge.svg)](https://github.com/techstartucalgary/fashion/actions/workflows/ci.yml) @@ -16,7 +16,7 @@ - [Lujaina]() - Project Manager - [Parsa]() - Frontend Developer -- [Morteza]() - Frontend Developer +- [Morteza]() - Full Stack Developer - [Axel Sanchez](https://github.com/Axeloooo) - Backend Developer - [Anfaal]() - Backend Developer - [Ryan]() - Backend Developer @@ -27,18 +27,22 @@ - Frontend ![Swift](https://img.shields.io/badge/Swift-F05138.svg?style=for-the-badge&logo=Swift&logoColor=white) - ![Xcode](https://img.shields.io/badge/Xcode-1575F9.svg?style=for-the-badge&logo=Xcode&logoColor=white) + ![SwiftUI](https://img.shields.io/badge/SwiftUI-2d68f3.svg?style=for-the-badge&logo=Swift&logoColor=black) ![Figma](https://img.shields.io/badge/Figma-F24E1E.svg?style=for-the-badge&logo=Figma&logoColor=white) + ![Xcode](https://img.shields.io/badge/Xcode-1575F9.svg?style=for-the-badge&logo=Xcode&logoColor=white) - Backend - ![Typescript](https://img.shields.io/badge/TypeScript-3178C6.svg?style=for-the-badge&logo=TypeScript&logoColor=white) + ![Typescript](https://img.shields.io/badge/TypeScript-1575F9.svg?style=for-the-badge&logo=TypeScript&logoColor=white) ![Node.js](https://img.shields.io/badge/Node.js-339933.svg?style=for-the-badge&logo=nodedotjs&logoColor=white) ![Express](https://img.shields.io/badge/Express-000000.svg?style=for-the-badge&logo=Express&logoColor=white) - ![OpenAI](https://img.shields.io/badge/OpenAI-412991.svg?style=for-the-badge&logo=OpenAI&logoColor=white) - ![Prisma](https://img.shields.io/badge/Prisma-2D3748.svg?style=for-the-badge&logo=Prisma&logoColor=white) - ![MySQL](https://img.shields.io/badge/MySQL-4479A1.svg?style=for-the-badge&logo=MySQL&logoColor=white) - ![Redis](https://img.shields.io/badge/Redis-DC382D.svg?style=for-the-badge&logo=Redis&logoColor=white) + ![OpenAI](https://img.shields.io/badge/OpenAI-00b48c.svg?style=for-the-badge&logo=OpenAI&logoColor=white) + +- Database + + ![Prisma](https://img.shields.io/badge/Prisma-5a67d8.svg?style=for-the-badge&logo=Prisma&logoColor=white) + ![MySQL](https://img.shields.io/badge/MySQL-3e6e93.svg?style=for-the-badge&logo=MySQL&logoColor=white) + ![Redis](https://img.shields.io/badge/redis-DC382D.svg?style=for-the-badge&logo=redis&logoColor=white) - Testing @@ -47,7 +51,8 @@ - CI/CD - ![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-2088FF.svg?style=for-the-badge&logo=GitHub%20Actions&logoColor=white) + ![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-000000.svg?style=for-the-badge&logo=GitHub%20Actions&logoColor=white) + ![Docker](https://img.shields.io/badge/Docker-2496ED.svg?style=for-the-badge&logo=Docker&logoColor=white) ## πŸš€ Backend Documentation @@ -79,7 +84,13 @@ cd backend npm install ``` -5. Run `npm run start` to start the server. +5. Run `docker-compose up -d` to start the database. + +```bash +docker-compose up -d +``` + +6. Run `npm run start` to start the server. ```bash npm run start @@ -103,25 +114,31 @@ npm -v 4. If you see the version number of `Node.js` and `npm` then you are good to go. If not, then try to reinstall `Node.js`. -5. Make sure you have `MySQL` installed on your machine. Click [here](https://dev.mysql.com/downloads/mysql/) to download and install MySQL. Make sure you install the latest version. +5. Make sure you have `Docker Desktop` installed on your machine. Click [here](https://www.docker.com/products/docker-desktop) to download and install Docker Desktop. Make sure you install the latest version. -6. Open the terminal and run `mysql --version` to check if `MySQL` is installed. +6. Open the terminal and run `docker -v` to check if `Docker` is installed. ```bash -mysql --version +docker -v ``` -7. If you see the version number of `MySQL` then you are good to go. If not, then try to reinstall `MySQL`. +8. In the same terminal run `docker-compose -v` to check if `Docker Compose` is installed. -8. Make sure you have `Redis` installed on your machine. Click [here](https://redis.io/download) to download and install Redis. Make sure you install the latest version. +```bash +docker-compose -v +``` + +9. If you see the version number of `Docker` and `Docker Compose` then you are good to go. If not, then try to reinstall `Docker Desktop`. + +10. Make sure you have `Git` installed on your machine. Click [here](https://git-scm.com/downloads) to download and install Git. Make sure you install the latest version. -9. Open the terminal and run `redis-server --version` to check if `Redis` is installed. +11. Open the terminal and run `git -v` to check if `Git` is installed. ```bash -redis-server --version +git -v ``` -10. If you see the version number of `Redis` then you are good to go. If not, then try to reinstall `Redis`. +12. If you see the version number of `Git` then you are good to go. If not, then try to reinstall `Git`. ### πŸ§ͺ Testing @@ -177,13 +194,19 @@ cd backend npm install ``` -5. Run `npm run start` to start the server. +5. Run `docker-compose up -d` to start the database. + +```bash +docker-compose up -d +``` + +6. Run `npm run dev` to start the development server. ```bash npm run dev ``` -6. Run `npx prisma studio` to open Prisma Studio and view the database schema (Optional). +7. Run `npx prisma studio` to open Prisma Studio and view the database schema (Optional). ```bash npx prisma studio From c0ecdac1a9e5a88fbd75bf51b453baa7964e0bf2 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sat, 13 Jan 2024 18:54:39 -0700 Subject: [PATCH 11/23] feat: Backend cleaned up and connection with database created --- .DS_Store | Bin 0 -> 6148 bytes .github/workflows/ci.yml | 34 +--- README.md | 68 +++----- backend/.gitignore | 3 + backend/docker-compose.yml | 20 --- backend/package-lock.json | 164 +----------------- backend/package.json | 12 +- backend/prisma/schema.prisma | 15 +- backend/src/config/config.ts | 2 - .../controllers/authentication.controller.ts | 35 ---- backend/src/index.ts | 8 +- backend/src/middlewares/session.middleware.ts | 19 -- backend/src/models/user.model.ts | 42 ----- backend/src/repositories/user.repository.ts | 34 ---- backend/src/routers/authentication.router.ts | 30 ---- .../algorithm.routes.ts} | 0 backend/test/algorithm.test.ts | 32 +++- backend/test/authentication.test.ts | 7 - backend/tsconfig.json | 2 +- package-lock.json | 6 + 20 files changed, 78 insertions(+), 455 deletions(-) create mode 100644 .DS_Store create mode 100644 backend/.gitignore delete mode 100644 backend/docker-compose.yml delete mode 100644 backend/src/controllers/authentication.controller.ts delete mode 100644 backend/src/middlewares/session.middleware.ts delete mode 100644 backend/src/models/user.model.ts delete mode 100644 backend/src/repositories/user.repository.ts delete mode 100644 backend/src/routers/authentication.router.ts rename backend/src/{routers/algorithm.router.ts => routes/algorithm.routes.ts} (100%) delete mode 100644 backend/test/authentication.test.ts create mode 100644 package-lock.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ef7498bb02e64e21613432745f7ce2f9bbfec1aa GIT binary patch literal 6148 zcmeHK&x;c=6n@!`HgyqtSYh`z=yj>>uC9oeT4xU)ymUkbl{(WY?XWYG(jWFv3UgTy zy@~!EUOjjaVG(b8QSj!;e?#9(Qk>~nk0O!>$(Ovmm%My$nlu2wTGOBbPz3;vh0r>O z)o+BxsT5>GYsi_=98tgFc11iaSQ>?4z%cMHG9Z8WP=h{%Fvp%*7cH_ee7^Rs{pwi8urg09 zenqn0Di@DD|1cgOJm|s^^dN!Yb|kub#}qE@w)R5`wkZ0XwLcpVwe?$n7=`0Wz5a!5 zl&mvn%e>4t`D1q=X70qBOvf#6^pJXYg^2uR+w<>*y;-NabxR}@FHCwP=@14zGGHFtEY^oewq^!luSl zp*%XUl1~6c3)NDvE$?ZrG+Ik2WeQyT^h19=8Ys#~S||8V{LKksCEh5^ICf5iZ^9M@^#mh|0P*c{!p vHkKz?NXRZ#C|$79*RgESRlJQw3Xa*-Kx}GE6`}^k{1K2en94BlR~h&Tp612@ literal 0 HcmV?d00001 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c43dc1b..fdf910f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,41 +5,17 @@ on: branches: - main - dev - + jobs: - testing: + build: runs-on: ubuntu-latest - services: - mysql: - image: mysql:5.7 - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: techstart-fashion - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h localhost -uroot -proot" - --health-interval=10s - --health-timeout=5s - --health-retries=5 - - env: - DATABASE_URL: mysql://root:root@localhost:3306/techstart-fashion steps: - name: Checkout uses: actions/checkout@v3 - + - name: Install dependencies run: cd backend && npm install - - name: Wait for database - run: | - until nc -z localhost 3306; do - echo "Waiting for MySQL to be ready..." - sleep 2 - done - echo "MySQL is ready." - - - name: Test - run: cd backend && npm run test + - name: Build + run: cd backend && npm run build diff --git a/README.md b/README.md index 1a3e46b..2014a48 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ - [Lujaina]() - Project Manager - [Parsa]() - Frontend Developer - [Morteza]() - Full Stack Developer -- [Axel Sanchez](https://github.com/Axeloooo) - Backend Developer +- [Alison]() - Backend Developer +- [Axel](https://github.com/Axeloooo) - Backend Developer - [Anfaal]() - Backend Developer - [Ryan]() - Backend Developer -- [Alison]() - Backend Developer ## πŸ‘¨β€πŸ’» Teck Stack @@ -54,6 +54,14 @@ ![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-000000.svg?style=for-the-badge&logo=GitHub%20Actions&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-2496ED.svg?style=for-the-badge&logo=Docker&logoColor=white) +- Deployment + + ![App Store](https://img.shields.io/badge/App%20Store-0D96F6.svg?style=for-the-badge&logo=App%20Store&logoColor=white) + ![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white) + ![Terraform](https://img.shields.io/badge/Terraform-623CE4.svg?style=for-the-badge&logo=Terraform&logoColor=white) + ![PlanetScale](https://img.shields.io/badge/PlanetScale-000000.svg?style=for-the-badge&logo=PlanetScale&logoColor=white) + ![Render](https://img.shields.io/badge/Render-46E3B7.svg?style=for-the-badge&logo=Render&logoColor=white) + ## πŸš€ Backend Documentation All the code is located in the `backend/src` directory. The backend is written using [Node.js](https://nodejs.org/en/) and [Express](https://expressjs.com/). @@ -78,24 +86,6 @@ cd fashion cd backend ``` -4. Run `npm install --only=production` to install all the dependencies. - -```bash -npm install -``` - -5. Run `docker-compose up -d` to start the database. - -```bash -docker-compose up -d -``` - -6. Run `npm run start` to start the server. - -```bash -npm run start -``` - ### πŸ› οΈ Installation 1. Make sure you have `Node.js` and `NPM` installed on your machine. Click [here](https://nodejs.org/en/) to download and install Node.js. Make sure you install the LTS version. NPM is installed automatically when you install Node.js. @@ -116,29 +106,15 @@ npm -v 5. Make sure you have `Docker Desktop` installed on your machine. Click [here](https://www.docker.com/products/docker-desktop) to download and install Docker Desktop. Make sure you install the latest version. -6. Open the terminal and run `docker -v` to check if `Docker` is installed. +6. Make sure you have `Git` installed on your machine. Click [here](https://git-scm.com/downloads) to download and install Git. Make sure you install the latest version. -```bash -docker -v -``` - -8. In the same terminal run `docker-compose -v` to check if `Docker Compose` is installed. - -```bash -docker-compose -v -``` - -9. If you see the version number of `Docker` and `Docker Compose` then you are good to go. If not, then try to reinstall `Docker Desktop`. - -10. Make sure you have `Git` installed on your machine. Click [here](https://git-scm.com/downloads) to download and install Git. Make sure you install the latest version. - -11. Open the terminal and run `git -v` to check if `Git` is installed. +7. Open the terminal and run `git -v` to check if `Git` is installed. ```bash git -v ``` -12. If you see the version number of `Git` then you are good to go. If not, then try to reinstall `Git`. +8. If you see the version number of `Git` then you are good to go. If not, then try to reinstall `Git`. ### πŸ§ͺ Testing @@ -194,20 +170,24 @@ cd backend npm install ``` -5. Run `docker-compose up -d` to start the database. +5. Run `npx prisma init` to initialize the database. ```bash -docker-compose up -d +npx prisma init ``` -6. Run `npm run dev` to start the development server. +6. Update your `prisma/schema.prisma` file within the `backend` folder to use the `mysql` provider and set the relation mode type to `prisma`. -```bash -npm run dev +```prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" +} ``` -7. Run `npx prisma studio` to open Prisma Studio and view the database schema (Optional). +7. Once you are ready to push your schema to PlanetScale, run `prisma db push`` against your PlanetScale database to update the schema in your database. ```bash -npx prisma studio +npx prisma db push ``` diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..11ddd8d --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,3 @@ +node_modules +# Keep environment variables out of version control +.env diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml deleted file mode 100644 index afab97b..0000000 --- a/backend/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: "3" - -services: - mysql: - image: mysql - environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE: ${MYSQL_DATABASE} - ports: - - 3306:3306 - volumes: - - mysql:/var/lib/mysql - - redis: - image: redis - ports: - - 6379:6379 - -volumes: - mysql: diff --git a/backend/package-lock.json b/backend/package-lock.json index 3d0a191..354d682 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,18 +10,14 @@ "license": "ISC", "dependencies": { "@prisma/client": "^5.6.0", - "connect-redis": "^7.1.0", "cors": "^2.8.5", "dotenv": "^16.3.1", - "express": "^4.18.2", - "express-session": "^1.17.3", - "redis": "^4.6.10" + "express": "^4.18.2" }, "devDependencies": { "@types/chai": "^4.3.10", "@types/cors": "^2.8.16", "@types/express": "^4.17.21", - "@types/express-session": "^1.17.10", "@types/mocha": "^10.0.4", "@types/node": "^20.9.0", "@types/supertest": "^2.0.16", @@ -65,59 +61,6 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz", "integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==" }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.5.11", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.11.tgz", - "integrity": "sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA==", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", - "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", - "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.5.tgz", - "integrity": "sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", - "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -182,15 +125,6 @@ "@types/send": "*" } }, - "node_modules/@types/express-session": { - "version": "1.17.10", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.10.tgz", - "integrity": "sha512-U32bC/s0ejXijw5MAzyaV4tuZopCh/K7fPoUDyNbsRXHvPSeymygYD1RFL99YOLhF5PNOkzswvOTRaVHdL1zMw==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -570,14 +504,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -620,17 +546,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/connect-redis": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.0.tgz", - "integrity": "sha512-UaqO1EirWjON2ENsyau7N5lbkrdYBpS6mYlXSeff/OYXsd6EGZ+SXSmNPoljL2PSua8fgjAEaldSA73PMZQ9Eg==", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "express-session": ">=1" - } - }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -876,32 +791,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express-session": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", - "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", - "dependencies": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -1035,14 +924,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "engines": { - "node": ">= 4" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1676,14 +1557,6 @@ "node": ">= 0.8" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1823,14 +1696,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1874,19 +1739,6 @@ "node": ">=8.10.0" } }, - "node_modules/redis": { - "version": "4.6.10", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.10.tgz", - "integrity": "sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg==", - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.11", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.6", - "@redis/search": "1.1.5", - "@redis/time-series": "1.0.5" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2223,17 +2075,6 @@ "node": ">=14.17" } }, - "node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -2311,7 +2152,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yargs": { "version": "16.2.0", diff --git a/backend/package.json b/backend/package.json index 9f49cff..3d7ebd0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,29 +7,25 @@ "scripts": { "build": "npx tsc", "prestart": "npx prisma migrate deploy && npx prisma generate", - "start": "NODE_ENV=PROD node dist/src/index.js", + "start": "node dist/src/index.js", "predev": "npx prisma migrate dev --name init && npx prisma generate", - "dev": "NODE_ENV=DEV nodemon -e ts --exec \"npm run build && node dist/src/index.js\"", + "dev": "nodemon -e ts --exec \"npm run build && node dist/src/index.js\"", "pretest": "npx prisma migrate dev --name init && npx prisma generate", - "test": "NODE_ENV=TEST npm run build && mocha \"dist/test/**/*.test.js\"" + "test": "npm run build && mocha \"dist/test/**/*.test.js\"" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@prisma/client": "^5.6.0", - "connect-redis": "^7.1.0", "cors": "^2.8.5", "dotenv": "^16.3.1", - "express": "^4.18.2", - "express-session": "^1.17.3", - "redis": "^4.6.10" + "express": "^4.18.2" }, "devDependencies": { "@types/chai": "^4.3.10", "@types/cors": "^2.8.16", "@types/express": "^4.17.21", - "@types/express-session": "^1.17.10", "@types/mocha": "^10.0.4", "@types/node": "^20.9.0", "@types/supertest": "^2.0.16", diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 2a2f383..02dcba2 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -3,16 +3,7 @@ generator client { } datasource db { - provider = "mysql" - url = env("DATABASE_URL") -} - -model User { - id String @id @default(uuid()) - email String @unique - password String - firstName String - lastName String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" } diff --git a/backend/src/config/config.ts b/backend/src/config/config.ts index 8afa7c2..c14de29 100644 --- a/backend/src/config/config.ts +++ b/backend/src/config/config.ts @@ -2,5 +2,3 @@ import dotenv from "dotenv"; dotenv.config(); export const PORT: number = Number(process.env.PORT) || 8080; -export const SESSION_SECRET: string = process.env.SESSION_SECRET || "secret"; -export const REDIS_URL: string = process.env.REDIS_URL || "redis"; diff --git a/backend/src/controllers/authentication.controller.ts b/backend/src/controllers/authentication.controller.ts deleted file mode 100644 index e8aaf8c..0000000 --- a/backend/src/controllers/authentication.controller.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { - AuthenticationControllerInterface, - AuthenticationServiceInterface, -} from "../../types.js"; - -class AuthenticationController implements AuthenticationControllerInterface { - constructor(private authentication: AuthenticationServiceInterface) {} - - postLogin = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise>> => { - // TODO: Implement postLogin controller (Anfaal) - }; - - postSignup = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise>> => { - // TODO: Implement postSignup controller (Ryan) - }; - - postLogout = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise>> => { - // TODO: Implement postLogout controller (Alison) - }; -} - -export default AuthenticationController; diff --git a/backend/src/index.ts b/backend/src/index.ts index 8b12ff8..a699745 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,11 +2,9 @@ import express, { Express } from "express"; import cors from "cors"; import { PORT } from "./config/config.js"; -import session from "./middlewares/session.middleware.js"; import errorHandler from "./middlewares/error.middleware.js"; -import algorithmRouter from "./routers/algorithm.router.js"; -import authenticationRouter from "./routers/authentication.router.js"; +import algorithmRouter from "./routes/algorithm.routes.js"; export const app: Express = express(); @@ -14,10 +12,6 @@ app.use(express.json()); app.use(cors()); -app.use(session); - -app.use("/auth", authenticationRouter); - app.use("/api/v1/algorithm", algorithmRouter); app.use(errorHandler); diff --git a/backend/src/middlewares/session.middleware.ts b/backend/src/middlewares/session.middleware.ts deleted file mode 100644 index e9206f9..0000000 --- a/backend/src/middlewares/session.middleware.ts +++ /dev/null @@ -1,19 +0,0 @@ -import session from "express-session"; -import redis from "redis"; -import { SESSION_SECRET, REDIS_URL } from "../config/config"; -const RedisStore = require("connect-redis")(session); - -const redisClient = redis.createClient({ - url: REDIS_URL, -}); - -export default session({ - store: new RedisStore({ client: redisClient }), - secret: SESSION_SECRET, - cookie: { - secure: process.env.NODE_ENV === "PROD", - sameSite: process.env.NODE_ENV === "PROD" ? "none" : "lax", - maxAge: - process.env.NODE_ENV === "TEST" ? 1000 * 60 * 5 : 1000 * 60 * 60 * 24 * 7, - }, -}); diff --git a/backend/src/models/user.model.ts b/backend/src/models/user.model.ts deleted file mode 100644 index 0a3372e..0000000 --- a/backend/src/models/user.model.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { UserInterface } from "../../types.js"; - -class User implements UserInterface { - constructor( - private email: string, - private password: string, - private firstName: string, - private lastName: string - ) {} - - getEmail(): string { - return this.email; - } - - getPassword(): string { - return this.password; - } - - getFirstName(): string { - return this.firstName; - } - - getLastName(): string { - return this.lastName; - } - - setEmail(email: string): void { - this.email = email; - } - - setPassword(password: string): void { - this.password = password; - } - - setFirstName(firstName: string): void { - this.firstName = firstName; - } - - setLastName(lastName: string): void { - this.lastName = lastName; - } -} diff --git a/backend/src/repositories/user.repository.ts b/backend/src/repositories/user.repository.ts deleted file mode 100644 index f1cb1e8..0000000 --- a/backend/src/repositories/user.repository.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import { - PrismaUserInterface, - UserInterface, - UserRepositoryInterface, -} from "../../types.js"; - -class UserRepository implements UserRepositoryInterface { - constructor(private prisma: PrismaClient) {} - - async createUser(user: UserInterface): Promise { - const newUser = await this.prisma.user.create({ - data: { - email: user.getEmail(), - password: user.getPassword(), - firstName: user.getFirstName(), - lastName: user.getLastName(), - }, - }); - return newUser; - } - - async getUserByEmail(email: string): Promise { - const user = await this.prisma.user.findUnique({ - where: { - email: email, - }, - }); - - return user; - } -} - -export default UserRepository; diff --git a/backend/src/routers/authentication.router.ts b/backend/src/routers/authentication.router.ts deleted file mode 100644 index 7f074fc..0000000 --- a/backend/src/routers/authentication.router.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Router } from "express"; -import { PrismaClient } from "@prisma/client"; -import UserRepository from "../repositories/user.repository.js"; -import AuthenticationService from "../services/authentication.service.js"; -import AuthenticationController from "../controllers/authentication.controller.js"; -import { - AuthenticationControllerInterface, - AuthenticationServiceInterface, - UserRepositoryInterface, -} from "../../types.js"; - -const authenticationRouter = Router(); - -const prisma = new PrismaClient(); - -const userRepository: UserRepositoryInterface = new UserRepository(prisma); - -const authenticationService: AuthenticationServiceInterface = - new AuthenticationService(userRepository); - -const authenticationController: AuthenticationControllerInterface = - new AuthenticationController(authenticationService); - -authenticationRouter.post("/login", authenticationController.postLogin); - -authenticationRouter.post("/signup", authenticationController.postSignup); - -authenticationRouter.post("/logout", authenticationController.postLogout); - -export default authenticationRouter; diff --git a/backend/src/routers/algorithm.router.ts b/backend/src/routes/algorithm.routes.ts similarity index 100% rename from backend/src/routers/algorithm.router.ts rename to backend/src/routes/algorithm.routes.ts diff --git a/backend/test/algorithm.test.ts b/backend/test/algorithm.test.ts index 3b5b72c..be97a91 100644 --- a/backend/test/algorithm.test.ts +++ b/backend/test/algorithm.test.ts @@ -12,25 +12,49 @@ const testString3: String = "100% cotton Machine wash cold Tumble dry low Not for use as pants"; describe("Algorithm", () => { + it("should return eco-friendly clothing", async () => { + const res: request.Response = await http + .post("/api/v1/algorithm") + .send({ prompt: testString3 }); + assert.equal(res.status, 200); + assert.equal( + res.body, + "Clothing is eco-friendly!", + "Body should be 'Clothing is eco-friendly!'" + ); + }); + it("should return eco-friendly clothing", async () => { const res: request.Response = await http .post("/api/v1/algorithm") .send({ prompt: testString2 }); assert.equal(res.status, 200); - assert.equal(res.body, "Clothing is eco-friendly!"); + assert.equal( + res.body, + "Clothing is eco-friendly!", + "Body should be 'Clothing is eco-friendly!'" + ); }); it("should return not eco-friendly clothing", async () => { const res: request.Response = await http .post("/api/v1/algorithm") .send({ prompt: testString1 }); - assert.equal(res.status, 200); - assert.equal(res.body, "Clothing is not eco-friendly!"); + assert.equal(res.status, 200, "Status code should be 200"); + assert.equal( + res.body, + "Clothing is not eco-friendly!", + "Body should be 'Clothing is not eco-friendly!'" + ); }); it("should return a BadRequestError", async () => { const res: request.Response = await http.post("/api/v1/algorithm").send({}); assert.equal(res.status, 400); - assert.equal(res.body, "No string was provided!"); + assert.equal( + res.body, + "No string was provided!", + "Body should be 'No string was provided!'" + ); }); }); diff --git a/backend/test/authentication.test.ts b/backend/test/authentication.test.ts deleted file mode 100644 index 16356d3..0000000 --- a/backend/test/authentication.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import request from "supertest"; -import { assert } from "chai"; -import { http } from "./config.test.js"; - -describe("Authentication", () => { - // TODO: Add tests for authentication and session management -}); diff --git a/backend/tsconfig.json b/backend/tsconfig.json index ab5f80a..ca771a3 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -9,7 +9,7 @@ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..459e9c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "fashion", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From 2f1bcce42b3f177fc7898ba835a27c4b5e3f3275 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sat, 13 Jan 2024 18:57:25 -0700 Subject: [PATCH 12/23] fix: Continuous integration issue with imports resolved --- .../src/services/authentication.service.ts | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 backend/src/services/authentication.service.ts diff --git a/backend/src/services/authentication.service.ts b/backend/src/services/authentication.service.ts deleted file mode 100644 index 0fba607..0000000 --- a/backend/src/services/authentication.service.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { UserRepositoryInterface } from "../../types"; -import UserRepository from "../repositories/user.repository"; - -class AuthenticationService implements AuthenticationService { - constructor(private UserRepository: UserRepositoryInterface) {} - - async login(): Promise { - // TODO: Implement login and initialize session (Anfaal) - } - - async signup(): Promise { - // TODO: Implement signup and initialize session (Ryan) - } - - async logout(): Promise { - // TODO: Implement logout and destroy session (Alison) - } -} - -export default AuthenticationService; From d2e4360dd64ba237d416b72d64dd897d326b65da Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sat, 13 Jan 2024 19:11:07 -0700 Subject: [PATCH 13/23] docs: Badges added, typo in tech corrected and instructions updated --- README.md | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 2014a48..5cc25f3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ -# ♻️ Fashion App +# ♻️ ReThread [![Continuous Integration](https://github.com/techstartucalgary/fashion/actions/workflows/ci.yml/badge.svg)](https://github.com/techstartucalgary/fashion/actions/workflows/ci.yml) +![GitHub repo size](https://img.shields.io/github/repo-size/techstartucalgary/rethread?logo=github&color=blue) +![Github tag](https://img.shields.io/github/v/tag/techstartucalgary/rethread?logo=github&color=red) +![GitHub contributors](https://img.shields.io/github/contributors/techstartucalgary/rethread?logo=github&color=yellow) +![Github pull requests](https://img.shields.io/github/issues-pr/techstartucalgary/rethread?logo=github) +![Github license](https://img.shields.io/github/license/techstartucalgary/rethread?logo=github&color=orange) ## πŸ“– Table of Contents - [πŸ“ Contributors](#-contributors) -- [πŸ‘¨β€πŸ’» Teck Stack](#-teck-stack) +- [πŸ‘¨β€πŸ’» Tech Stack](#-tech-stack) - [πŸš€ Backend Documentation](#-backend-documentation) - - [πŸƒ Quickstart](#-quickstart) + - [πŸƒ Quick start](#-quick-start) - [πŸ› οΈ Installation](#️-installation) - [πŸ§ͺ Testing](#-testing) - [🚧 Development Environment](#-development-environment) @@ -22,7 +27,7 @@ - [Anfaal]() - Backend Developer - [Ryan]() - Backend Developer -## πŸ‘¨β€πŸ’» Teck Stack +## πŸ‘¨β€πŸ’» Tech Stack - Frontend @@ -33,40 +38,17 @@ - Backend - ![Typescript](https://img.shields.io/badge/TypeScript-1575F9.svg?style=for-the-badge&logo=TypeScript&logoColor=white) - ![Node.js](https://img.shields.io/badge/Node.js-339933.svg?style=for-the-badge&logo=nodedotjs&logoColor=white) + ![Typescript](https://img.shields.io/badge/TypeScript-3178C6.svg?style=for-the-badge&logo=TypeScript&logoColor=white) ![Express](https://img.shields.io/badge/Express-000000.svg?style=for-the-badge&logo=Express&logoColor=white) - ![OpenAI](https://img.shields.io/badge/OpenAI-00b48c.svg?style=for-the-badge&logo=OpenAI&logoColor=white) - -- Database - + ![Node.js](https://img.shields.io/badge/Node.js-339933.svg?style=for-the-badge&logo=nodedotjs&logoColor=white) ![Prisma](https://img.shields.io/badge/Prisma-5a67d8.svg?style=for-the-badge&logo=Prisma&logoColor=white) ![MySQL](https://img.shields.io/badge/MySQL-3e6e93.svg?style=for-the-badge&logo=MySQL&logoColor=white) - ![Redis](https://img.shields.io/badge/redis-DC382D.svg?style=for-the-badge&logo=redis&logoColor=white) - -- Testing - - ![Mocha](https://img.shields.io/badge/Mocha-8D6748.svg?style=for-the-badge&logo=Mocha&logoColor=white) - ![Chai](https://img.shields.io/badge/Chai-A30701.svg?style=for-the-badge&logo=Chai&logoColor=white) - -- CI/CD - - ![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-000000.svg?style=for-the-badge&logo=GitHub%20Actions&logoColor=white) - ![Docker](https://img.shields.io/badge/Docker-2496ED.svg?style=for-the-badge&logo=Docker&logoColor=white) - -- Deployment - - ![App Store](https://img.shields.io/badge/App%20Store-0D96F6.svg?style=for-the-badge&logo=App%20Store&logoColor=white) - ![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white) - ![Terraform](https://img.shields.io/badge/Terraform-623CE4.svg?style=for-the-badge&logo=Terraform&logoColor=white) - ![PlanetScale](https://img.shields.io/badge/PlanetScale-000000.svg?style=for-the-badge&logo=PlanetScale&logoColor=white) - ![Render](https://img.shields.io/badge/Render-46E3B7.svg?style=for-the-badge&logo=Render&logoColor=white) ## πŸš€ Backend Documentation All the code is located in the `backend/src` directory. The backend is written using [Node.js](https://nodejs.org/en/) and [Express](https://expressjs.com/). -### πŸƒ Quickstart +### πŸƒ Quick start 1. Open the terminal and clone this repository using HTTPS or SSH (The example below uses SSH). From 3a37971a7360a671ae525b4d0d7674c82ae85077 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sat, 13 Jan 2024 19:15:57 -0700 Subject: [PATCH 14/23] docs: MIT license attached to the repository --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bbb7699 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 techstartucalgary + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From eab7aebfa0122bad63792e5d3c195ff9bf1bae9a Mon Sep 17 00:00:00 2001 From: aligstanley Date: Sun, 14 Jan 2024 15:22:14 -0700 Subject: [PATCH 15/23] database setup --- backend/prisma/schema.prisma | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 02dcba2..7c3625f 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -3,7 +3,28 @@ generator client { } datasource db { - provider = "mysql" - url = env("DATABASE_URL") - relationMode = "prisma" + provider = "mysql" + url = env("DATABASE_URL") } + +model Product { + id Int @id @default(uuid()) + title String @unique + size String + color String + description String + gender String + category String + price Float + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Purchase_history { + id Int @id @default(uuid()) + email String @unique + productId Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + From f93ace6b813e3c462c9a382711a88155d468c4d5 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Mon, 15 Jan 2024 21:05:35 -0700 Subject: [PATCH 16/23] feat: Prisma user model created --- .DS_Store | Bin 6148 -> 6148 bytes README.md | 2 +- backend/package.json | 3 --- backend/prisma/schema.prisma | 14 ++++++++++ backend/types.ts | 50 ----------------------------------- 5 files changed, 15 insertions(+), 54 deletions(-) diff --git a/.DS_Store b/.DS_Store index ef7498bb02e64e21613432745f7ce2f9bbfec1aa..d4d9e9973fcfac7deaec84240a82b4db1574a7c6 100644 GIT binary patch delta 181 zcmZoMXfc@J&nUDpU^g?P&}JT%6O1Cp=4Lty#>OVKItta6Mg}?xCT50{U$8!`XJ_zX z@MLgiaAojg2xf5g%*jtq%E?b+U|45V7?wh7DF+Rorh*ZH<}5Y3_(D<92r~~e1Wvy; } - -export interface AuthenticationControllerInterface { - postLogin: ( - req: Request, - res: Response, - next: NextFunction - ) => Promise>>; - postSignup: ( - req: Request, - res: Response, - next: NextFunction - ) => Promise>>; - postLogout: ( - req: Request, - res: Response, - next: NextFunction - ) => Promise>>; -} - -export interface AuthenticationServiceInterface { - login(): Promise; - signup(): Promise; - logout(): Promise; -} - -export interface UserRepositoryInterface { - createUser(user: UserInterface): Promise; - getUserByEmail(email: string): Promise; -} - -export interface UserInterface { - getEmail(): string; - getPassword(): string; - getFirstName(): string; - getLastName(): string; - setEmail(email: string): void; - setPassword(password: string): void; - setFirstName(firstName: string): void; - setLastName(lastName: string): void; -} - -export interface PrismaUserInterface { - id: string; - email: string; - password: string; - firstName: string; - lastName: string; - createdAt: Date; - updatedAt: Date; -} From 8c00546c5fa603ba7f0024c6a9a75b9adb40faa5 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Mon, 15 Jan 2024 21:13:35 -0700 Subject: [PATCH 17/23] feat: Product and order prisma models updated --- backend/prisma/schema.prisma | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 7c3625f..2a07470 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -8,23 +8,23 @@ datasource db { } model Product { - id Int @id @default(uuid()) - title String @unique - size String - color String - description String - gender String - category String - price Float - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) + title String @unique + size String + color String + description String + gender String + category String + price Float + imageUrl String @map("image_url") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") } -model Purchase_history { - id Int @id @default(uuid()) - email String @unique - productId Int - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt +model Order { + id String @id @default(uuid()) + email String + productId Int @map("product_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") } - From 4679891d4683e46d227f87c5c2ed785367bf5142 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Mon, 15 Jan 2024 21:19:41 -0700 Subject: [PATCH 18/23] feat: Database synced with Planetscale --- backend/prisma/schema.prisma | 5 +++-- package-lock.json | 6 ------ 2 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 package-lock.json diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index a6ed55a..808daa4 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -3,8 +3,9 @@ generator client { } datasource db { - provider = "mysql" - url = env("DATABASE_URL") + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" } model Product { diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 459e9c7..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "fashion", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} From dc8df7b285ea2012c062cc0008edb76435db0361 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Mon, 15 Jan 2024 21:30:17 -0700 Subject: [PATCH 19/23] docs: PlanetScale badge added to documentation --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c5312c6..5dc480c 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,10 @@ ![Prisma](https://img.shields.io/badge/Prisma-5a67d8.svg?style=for-the-badge&logo=Prisma&logoColor=white) ![MySQL](https://img.shields.io/badge/MySQL-3e6e93.svg?style=for-the-badge&logo=MySQL&logoColor=white) +- Cloud + + ![PlanetScale](https://img.shields.io/badge/PlanetScale-000000.svg?style=for-the-badge&logo=PlanetScale&logoColor=white) + ## πŸš€ Backend Documentation All the code is located in the `backend/src` directory. The backend is written using [Node.js](https://nodejs.org/en/) and [Express](https://expressjs.com/). From 2d9a1ed6e387054236bf883b1c0359c25c06c00c Mon Sep 17 00:00:00 2001 From: ryanjung1998 Date: Wed, 17 Jan 2024 16:46:24 -0700 Subject: [PATCH 20/23] Created API --- backend/src/index.ts | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index a699745..5104c64 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,11 +1,12 @@ import express, { Express } from "express"; import cors from "cors"; import { PORT } from "./config/config.js"; - import errorHandler from "./middlewares/error.middleware.js"; import algorithmRouter from "./routes/algorithm.routes.js"; +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() export const app: Express = express(); app.use(express.json()); @@ -14,6 +15,32 @@ app.use(cors()); app.use("/api/v1/algorithm", algorithmRouter); +app.post("/product", async (request, response) => { + const {title, size, color, description, gender, category, price, imageUrl} = request.body + const newProduct = await prisma.product.create({ + data: { + title, size, color, description, gender, category, price, imageUrl + } + }) + response.json(newProduct) +}) + +app.get("/products", async (request, response) => { + //add filtering + const prods = await prisma.product.findMany(); + + response.json(prods); +}); + +app.get("/product/:id", async (request, response) => { + let prodID = request.params.id; + const product = await prisma.product.findUnique({ + where: {id: prodID} + }) + + response.json(product); +}); + app.use(errorHandler); app.listen(PORT, () => { From dd1ed408c769e1393abfa540406c9878aca1affe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20S=C3=A1nchez?= Date: Wed, 17 Jan 2024 19:20:49 -0700 Subject: [PATCH 21/23] Delete .DS_Store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Axel SΓ‘nchez --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index d4d9e9973fcfac7deaec84240a82b4db1574a7c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK&x;c=6n@!OJ9QDN?815w2EEqW+GQ8<((Sf~1@+Jo6;$d>r?lhFOiF)j52etH zAbJ!1JG^@EAi^Tv_M+g;lmCXkm!vo|wH~!d9wcAp<-O$Pduh@D0IQDN5MU|fmLLHe!FQXKnFY+#(ux4_m`r50k72@ zpF5~CJ{`aOdg0meUcBG^V}Gw3IU6?Uyn)PoUg*LIoY?G;`o3rpjp6g|yT+@hB8J2` z0sj@ra;sQ8;{3yS9I&AUhtP%)+ykGt)QTxwT5aKn5NuKOF>7Z$=qc;BPS5uS!(#CZ zTT7)+oz55;W8HXUb@|vD+QZSHYWE+|*B#D%XWq7*yIyyfu+O9hVnyLhB5F|d9|1vwt~3LGm4Tm-P~sN= From af4f80a7d1c10e08c79547cb004911463be58dca Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Wed, 17 Jan 2024 19:21:31 -0700 Subject: [PATCH 22/23] fix: .DS_Store file added to .gitignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 83c0810..51d6996 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .env dist migrations +.DS_Store \ No newline at end of file From 61a4c6b7305b7bf3bd96cd298f96fad7d8a39ab7 Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Wed, 17 Jan 2024 22:02:59 -0700 Subject: [PATCH 23/23] feat: Product router, controller, service and repository created --- backend/src/abstracts/product.abstract.ts | 34 +++++ .../src/controllers/algorithm.controller.ts | 8 +- backend/src/controllers/product.controller.ts | 70 ++++++++++ backend/src/errors/errors.ts | 27 ---- backend/src/errors/http.error.ts | 7 + backend/src/errors/prisma.error.ts | 11 ++ backend/src/errors/product.error.ts | 1 + backend/src/index.ts | 32 +---- backend/src/middlewares/error.middleware.ts | 55 +++++--- .../src/repositories/product.repository.ts | 130 ++++++++++++++++++ backend/src/routes/algorithm.routes.ts | 1 - backend/src/routes/product.routes.ts | 16 +++ backend/src/services/product.service.ts | 55 ++++++++ 13 files changed, 367 insertions(+), 80 deletions(-) create mode 100644 backend/src/abstracts/product.abstract.ts create mode 100644 backend/src/controllers/product.controller.ts delete mode 100644 backend/src/errors/errors.ts create mode 100644 backend/src/errors/http.error.ts create mode 100644 backend/src/errors/prisma.error.ts create mode 100644 backend/src/errors/product.error.ts create mode 100644 backend/src/repositories/product.repository.ts create mode 100644 backend/src/routes/product.routes.ts create mode 100644 backend/src/services/product.service.ts diff --git a/backend/src/abstracts/product.abstract.ts b/backend/src/abstracts/product.abstract.ts new file mode 100644 index 0000000..3f29ad9 --- /dev/null +++ b/backend/src/abstracts/product.abstract.ts @@ -0,0 +1,34 @@ +type PrismaProduct = { + id: string; + title: string; + size: string; + color: string; + description: string; + gender: string; + category: string; + price: number; + imageUrl: string; + createdAt: Date; + updatedAt: Date; +}; + +type PrismaProducts = PrismaProduct[]; + +abstract class ProductProvider { + abstract getProducts(): Promise; + + abstract getProductById(id: string): Promise; + + abstract createProduct( + title: string, + size: string, + color: string, + description: string, + gender: string, + category: string, + price: number, + imageUrl: string + ): Promise; + + abstract deleteProduct(id: string): Promise; +} diff --git a/backend/src/controllers/algorithm.controller.ts b/backend/src/controllers/algorithm.controller.ts index cdeaff5..a1d09ba 100644 --- a/backend/src/controllers/algorithm.controller.ts +++ b/backend/src/controllers/algorithm.controller.ts @@ -1,5 +1,5 @@ import { Request, Response, NextFunction } from "express"; -import { BadRequestError } from "../errors/errors.js"; +import { HttpBadRequestError } from "../errors/http.error.js"; import { AlgorithmServiceInterface } from "../../types.js"; class AlgorithmController { @@ -13,7 +13,7 @@ class AlgorithmController { try { const prompt: string = req.body.prompt; if (!prompt) { - return next(new BadRequestError("No string was provided!")); + return next(new HttpBadRequestError()); } const isEcoFriendly: boolean = @@ -23,8 +23,8 @@ class AlgorithmController { return res.status(200).json("Clothing is not eco-friendly!"); } return res.status(200).json("Clothing is eco-friendly!"); - } catch (err) { - next(err); + } catch (e) { + next(e); } }; } diff --git a/backend/src/controllers/product.controller.ts b/backend/src/controllers/product.controller.ts new file mode 100644 index 0000000..9af12be --- /dev/null +++ b/backend/src/controllers/product.controller.ts @@ -0,0 +1,70 @@ +import { Request, Response, NextFunction } from "express"; + +class ProductController { + constructor(private service: ProductProvider) { + this.service = service; + } + + getProducts = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise> | void> => { + try { + const products = await this.service.getProducts(); + return res.status(200).json(products); + } catch (e) { + next(e); + } + }; + + getProductById = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise> | void> => { + try { + const product = await this.service.getProductById(req.params.id); + return res.status(200).json(product); + } catch (e) { + next(e); + } + }; + + postProduct = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise> | void> => { + try { + const newProduct = await this.service.createProduct( + req.body.title, + req.body.size, + req.body.color, + req.body.description, + req.body.gender, + req.body.category, + req.body.price, + req.body.imageUrl + ); + return res.status(201).json(newProduct); + } catch (e) { + next(e); + } + }; + + deleteProduct = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise> | void> => { + try { + const product = await this.service.deleteProduct(req.params.id); + return res.status(200).json(product); + } catch (e) { + next(e); + } + }; +} + +export default ProductController; diff --git a/backend/src/errors/errors.ts b/backend/src/errors/errors.ts deleted file mode 100644 index 1450740..0000000 --- a/backend/src/errors/errors.ts +++ /dev/null @@ -1,27 +0,0 @@ -export class BadRequestError extends Error { - constructor(public message: string) { - super(message); - this.name = "BadRequestError"; - } -} - -export class UnauthorizedError extends Error { - constructor(public message: string) { - super(message); - this.name = "UnauthorizedError"; - } -} - -export class ForbiddenError extends Error { - constructor(public message: string) { - super(message); - this.name = "ForbiddenError"; - } -} - -export class NotFoundError extends Error { - constructor(public message: string) { - super(message); - this.name = "NotFoundError"; - } -} diff --git a/backend/src/errors/http.error.ts b/backend/src/errors/http.error.ts new file mode 100644 index 0000000..3b10d55 --- /dev/null +++ b/backend/src/errors/http.error.ts @@ -0,0 +1,7 @@ +export class HttpBadRequestError extends Error {} + +export class HttpUnauthorizedError extends Error {} + +export class HttpForbiddenError extends Error {} + +export class HttpNotFoundError extends Error {} diff --git a/backend/src/errors/prisma.error.ts b/backend/src/errors/prisma.error.ts new file mode 100644 index 0000000..7f1c0c5 --- /dev/null +++ b/backend/src/errors/prisma.error.ts @@ -0,0 +1,11 @@ +export class PrismaClientKnownRequestError extends Error {} + +export class PrismaClientUnknownRequestError extends Error {} + +export class PrismaClientRustPanicError extends Error {} + +export class PrismaClientInitializationError extends Error {} + +export class PrismaClientValidationError extends Error {} + +export class PrismaGenericError extends Error {} diff --git a/backend/src/errors/product.error.ts b/backend/src/errors/product.error.ts new file mode 100644 index 0000000..71f53fb --- /dev/null +++ b/backend/src/errors/product.error.ts @@ -0,0 +1 @@ +export class ProductNotFoundError extends Error {} diff --git a/backend/src/index.ts b/backend/src/index.ts index 5104c64..81ed20c 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,12 +2,12 @@ import express, { Express } from "express"; import cors from "cors"; import { PORT } from "./config/config.js"; import errorHandler from "./middlewares/error.middleware.js"; - +import { PrismaClient } from "@prisma/client"; import algorithmRouter from "./routes/algorithm.routes.js"; +import productRouter from "./routes/product.routes.js"; -import { PrismaClient } from '@prisma/client' -const prisma = new PrismaClient() export const app: Express = express(); +export const prisma = new PrismaClient(); app.use(express.json()); @@ -15,31 +15,7 @@ app.use(cors()); app.use("/api/v1/algorithm", algorithmRouter); -app.post("/product", async (request, response) => { - const {title, size, color, description, gender, category, price, imageUrl} = request.body - const newProduct = await prisma.product.create({ - data: { - title, size, color, description, gender, category, price, imageUrl - } - }) - response.json(newProduct) -}) - -app.get("/products", async (request, response) => { - //add filtering - const prods = await prisma.product.findMany(); - - response.json(prods); -}); - -app.get("/product/:id", async (request, response) => { - let prodID = request.params.id; - const product = await prisma.product.findUnique({ - where: {id: prodID} - }) - - response.json(product); -}); +app.use("/api/v1/products", productRouter); app.use(errorHandler); diff --git a/backend/src/middlewares/error.middleware.ts b/backend/src/middlewares/error.middleware.ts index 38ddd85..becceca 100644 --- a/backend/src/middlewares/error.middleware.ts +++ b/backend/src/middlewares/error.middleware.ts @@ -1,30 +1,45 @@ import { Request, Response, NextFunction } from "express"; import { - BadRequestError, - ForbiddenError, - NotFoundError, - UnauthorizedError, -} from "../errors/errors.js"; - + HttpBadRequestError, + HttpForbiddenError, + HttpNotFoundError, + HttpUnauthorizedError, +} from "../errors/http.error.js"; +import { + PrismaClientInitializationError, + PrismaClientRustPanicError, + PrismaClientUnknownRequestError, + PrismaClientValidationError, + PrismaGenericError, +} from "../errors/prisma.error.js"; +import { ProductNotFoundError } from "../errors/product.error.js"; export default function errorHandler( - err: Error, + e: Error, req: Request, res: Response, next: NextFunction ) { - if (err instanceof BadRequestError) { - res.status(400).json(err.message); - } else if (err instanceof UnauthorizedError) { - res.status(401).json(err.message); - } else if (err instanceof ForbiddenError) { - res.status(403).json(err.message); - } else if (err instanceof NotFoundError) { - res.status(404).json(err.message); + if (e instanceof HttpBadRequestError) { + res.status(400).json({ error: "Bad Request Error" }); + } else if (e instanceof HttpUnauthorizedError) { + res.status(401).json({ error: "Unauthorized Error" }); + } else if (e instanceof HttpForbiddenError) { + res.status(403).json({ error: "Forbidden Error" }); + } else if (e instanceof HttpNotFoundError) { + res.status(404).json({ error: "Not Found Error" }); + } else if (e instanceof PrismaClientUnknownRequestError) { + res.status(500).json({ error: "Prisma Client Unknown Request Error" }); + } else if (e instanceof PrismaClientRustPanicError) { + res.status(500).json({ error: "Prisma Client Rust Panic Error" }); + } else if (e instanceof PrismaClientInitializationError) { + res.status(500).json({ error: "Prisma Client Initialization Error" }); + } else if (e instanceof PrismaClientValidationError) { + res.status(500).json({ error: "Prisma Client Validation Error" }); + } else if (e instanceof PrismaGenericError) { + res.status(500).json({ error: "Prisma Generic Error" }); + } else if (e instanceof ProductNotFoundError) { + res.status(500).json({ error: "Product Not Found Error" }); } else { - res.status(500).json({ - name: err.name, - message: err.message, - stack: err.stack, - }); + res.status(500).json({ error: "Unexpected error" }); } } diff --git a/backend/src/repositories/product.repository.ts b/backend/src/repositories/product.repository.ts new file mode 100644 index 0000000..d1e5e4c --- /dev/null +++ b/backend/src/repositories/product.repository.ts @@ -0,0 +1,130 @@ +import { prisma } from "../index.js"; +import { Prisma } from "@prisma/client"; +import { ProductNotFoundError } from "../errors/product.error.js"; +import { + PrismaClientInitializationError, + PrismaClientRustPanicError, + PrismaClientUnknownRequestError, + PrismaClientValidationError, + PrismaGenericError, +} from "../errors/prisma.error.js"; + +class ProductRepository implements ProductProvider { + getProducts = async (): Promise => { + try { + const products: PrismaProducts = await prisma.product.findMany(); + return products; + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + throw new PrismaGenericError(); + } else if (e instanceof Prisma.PrismaClientUnknownRequestError) { + throw new PrismaClientUnknownRequestError(); + } else if (e instanceof Prisma.PrismaClientRustPanicError) { + throw new PrismaClientRustPanicError(); + } else if (e instanceof Prisma.PrismaClientInitializationError) { + throw new PrismaClientInitializationError(); + } else if (e instanceof Prisma.PrismaClientValidationError) { + throw new PrismaClientValidationError(); + } else { + throw new PrismaGenericError(); + } + } + }; + + getProductById = async (id: string): Promise => { + try { + const product: PrismaProduct | null = await prisma.product.findUnique({ + where: { id: id }, + }); + if (product === null) { + throw new ProductNotFoundError(); + } + return product; + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + throw new PrismaGenericError(); + } else if (e instanceof Prisma.PrismaClientUnknownRequestError) { + throw new PrismaClientUnknownRequestError(); + } else if (e instanceof Prisma.PrismaClientRustPanicError) { + throw new PrismaClientRustPanicError(); + } else if (e instanceof Prisma.PrismaClientInitializationError) { + throw new PrismaClientInitializationError(); + } else if (e instanceof Prisma.PrismaClientValidationError) { + throw new PrismaClientValidationError(); + } else if (e instanceof ProductNotFoundError) { + throw new ProductNotFoundError(); + } else { + throw new PrismaGenericError(); + } + } + }; + + createProduct = async ( + title: string, + size: string, + color: string, + description: string, + gender: string, + category: string, + price: number, + imageUrl: string + ): Promise => { + try { + const newProduct: PrismaProduct = await prisma.product.create({ + data: { + title: title, + size: size, + color: color, + description: description, + gender: gender, + category: category, + price: price, + imageUrl: imageUrl, + }, + }); + return newProduct; + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + throw new PrismaGenericError(); + } else if (e instanceof Prisma.PrismaClientUnknownRequestError) { + throw new PrismaClientUnknownRequestError(); + } else if (e instanceof Prisma.PrismaClientRustPanicError) { + throw new PrismaClientRustPanicError(); + } else if (e instanceof Prisma.PrismaClientInitializationError) { + throw new PrismaClientInitializationError(); + } else if (e instanceof Prisma.PrismaClientValidationError) { + throw new PrismaClientValidationError(); + } else { + throw new PrismaGenericError(); + } + } + }; + + deleteProduct = async (id: string): Promise => { + try { + const product: PrismaProduct | null = await prisma.product.delete({ + where: { id: id }, + }); + if (product === null) { + throw new ProductNotFoundError(); + } + return product; + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + throw new PrismaGenericError(); + } else if (e instanceof Prisma.PrismaClientUnknownRequestError) { + throw new PrismaClientUnknownRequestError(); + } else if (e instanceof Prisma.PrismaClientRustPanicError) { + throw new PrismaClientRustPanicError(); + } else if (e instanceof Prisma.PrismaClientInitializationError) { + throw new PrismaClientInitializationError(); + } else if (e instanceof Prisma.PrismaClientValidationError) { + throw new PrismaClientValidationError(); + } else { + throw new PrismaGenericError(); + } + } + }; +} + +export default ProductRepository; diff --git a/backend/src/routes/algorithm.routes.ts b/backend/src/routes/algorithm.routes.ts index 465e668..8a34326 100644 --- a/backend/src/routes/algorithm.routes.ts +++ b/backend/src/routes/algorithm.routes.ts @@ -7,7 +7,6 @@ import { } from "../../types.js"; const algorithmRouter: Router = Router(); - const algorithmService: AlgorithmServiceInterface = new AlgorithmService(); const algorithmController: AlgorithmControllerInterface = new AlgorithmController(algorithmService); diff --git a/backend/src/routes/product.routes.ts b/backend/src/routes/product.routes.ts new file mode 100644 index 0000000..d6eba30 --- /dev/null +++ b/backend/src/routes/product.routes.ts @@ -0,0 +1,16 @@ +import { Router } from "express"; +import ProductController from "../controllers/product.controller.js"; +import ProductService from "../services/product.service.js"; +import ProductRepository from "../repositories/product.repository.js"; + +const productRouter = Router(); +const productController = new ProductController( + new ProductService(new ProductRepository()) +); + +productRouter.get("/", productController.getProducts); +productRouter.get("/:id", productController.getProductById); +productRouter.post("/", productController.postProduct); +productRouter.delete("/:id", productController.deleteProduct); + +export default productRouter; diff --git a/backend/src/services/product.service.ts b/backend/src/services/product.service.ts new file mode 100644 index 0000000..7adba95 --- /dev/null +++ b/backend/src/services/product.service.ts @@ -0,0 +1,55 @@ +import { HttpBadRequestError } from "../errors/http.error.js"; + +class ProductService implements ProductProvider { + constructor(private provider: ProductProvider) { + this.provider = provider; + } + + getProducts = async (): Promise => { + return await this.provider.getProducts(); + }; + + getProductById = async (id: string): Promise => { + return await this.provider.getProductById(id); + }; + + createProduct = async ( + title: string, + size: string, + color: string, + description: string, + gender: string, + category: string, + price: number, + imageUrl: string + ): Promise => { + if ( + !title || + !size || + !color || + !description || + !gender || + !category || + !price || + !imageUrl + ) { + throw new HttpBadRequestError(); + } + return await this.provider.createProduct( + title, + size, + color, + description, + gender, + category, + price, + imageUrl + ); + }; + + deleteProduct = async (id: string): Promise => { + return await this.provider.deleteProduct(id); + }; +} + +export default ProductService;