From 85f743222f1887828a7ac76aeed78c17aa1bd3b8 Mon Sep 17 00:00:00 2001 From: My CTO Date: Wed, 29 Nov 2023 10:18:41 +0100 Subject: [PATCH 1/4] Prisma schema & API to list instructors --- package-lock.json | 86 ++++++++++++++++++++++++++++-- package.json | 2 + prisma/schema.prisma | 51 ++++++++++++++++++ src/prisma.ts | 6 +++ src/server.ts | 28 +++++++++- src/server/handlers/instructors.ts | 20 +++++++ 6 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 prisma/schema.prisma create mode 100644 src/prisma.ts create mode 100644 src/server/handlers/instructors.ts diff --git a/package-lock.json b/package-lock.json index 5a22db1..7583af4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { - "name": "rabbi_typescript_starter", - "version": "0.5.0", + "name": "ts-backend-starter-app", + "version": "0.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "rabbi_typescript_starter", - "version": "0.5.0", + "name": "ts-backend-starter-app", + "version": "0.6.0", "dependencies": { "@hapi/hapi": "^20.2.2", "@hapi/inert": "^6.0.5", "@hapi/joi": "^17.1.1", "@hapi/vision": "^6.1.0", + "@prisma/client": "^5.6.0", "@promster/hapi": "^12.0.0", "commander": "^9.3.0", "dotenv": "^10.0.0", @@ -40,6 +41,7 @@ "commitizen": "^4.2.4", "cz-conventional-changelog": "^3.3.0", "mocha": "^9.1.2", + "prisma": "^5.6.0", "shebang-trim": "^1.1.0", "ts-node": "^10.9.1", "typedoc": "^0.23.24" @@ -1275,6 +1277,38 @@ "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" }, + "node_modules/@prisma/client": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.6.0.tgz", + "integrity": "sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==", + "hasInstallScript": true, + "dependencies": { + "@prisma/engines-version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee" + }, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/engines": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.6.0.tgz", + "integrity": "sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==", + "devOptional": true, + "hasInstallScript": true + }, + "node_modules/@prisma/engines-version": { + "version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz", + "integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==" + }, "node_modules/@promster/hapi": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@promster/hapi/-/hapi-12.0.0.tgz", @@ -6879,6 +6913,22 @@ "node": ">=4" } }, + "node_modules/prisma": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.6.0.tgz", + "integrity": "sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.6.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -10448,6 +10498,25 @@ "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" }, + "@prisma/client": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.6.0.tgz", + "integrity": "sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==", + "requires": { + "@prisma/engines-version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee" + } + }, + "@prisma/engines": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.6.0.tgz", + "integrity": "sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==", + "devOptional": true + }, + "@prisma/engines-version": { + "version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz", + "integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==" + }, "@promster/hapi": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@promster/hapi/-/hapi-12.0.0.tgz", @@ -14725,6 +14794,15 @@ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" }, + "prisma": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.6.0.tgz", + "integrity": "sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==", + "devOptional": true, + "requires": { + "@prisma/engines": "5.6.0" + } + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", diff --git a/package.json b/package.json index 4dcbff3..7aa0893 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@hapi/inert": "^6.0.5", "@hapi/joi": "^17.1.1", "@hapi/vision": "^6.1.0", + "@prisma/client": "^5.6.0", "@promster/hapi": "^12.0.0", "commander": "^9.3.0", "dotenv": "^10.0.0", @@ -48,6 +49,7 @@ "commitizen": "^4.2.4", "cz-conventional-changelog": "^3.3.0", "mocha": "^9.1.2", + "prisma": "^5.6.0", "shebang-trim": "^1.1.0", "ts-node": "^10.9.1", "typedoc": "^0.23.24" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..8d53c82 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,51 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + password String + username String + EmailPasswordAuth EmailPasswordAuth? + Instructor Instructor[] + JwtToken JwtToken[] + SocialMediaAuth SocialMediaAuth[] +} + +model EmailPasswordAuth { + id Int @id @default(autoincrement()) + userId Int @unique + password String + User User @relation(fields: [userId], references: [id]) +} + +model Instructor { + id Int @id @default(autoincrement()) + userId Int? + name String + position String? + slug String? + image_url String? + User User? @relation(fields: [userId], references: [id]) +} + +model JwtToken { + id Int @id @default(autoincrement()) + userId Int + token String + User User @relation(fields: [userId], references: [id]) +} + +model SocialMediaAuth { + id Int @id @default(autoincrement()) + userId Int + provider String + socialId String + User User @relation(fields: [userId], references: [id]) +} diff --git a/src/prisma.ts b/src/prisma.ts new file mode 100644 index 0000000..471c707 --- /dev/null +++ b/src/prisma.ts @@ -0,0 +1,6 @@ + +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export default prisma diff --git a/src/server.ts b/src/server.ts index 5b5bd91..8d29458 100644 --- a/src/server.ts +++ b/src/server.ts @@ -55,7 +55,8 @@ if (config.get('prometheus_enabled')) { } server.route({ - method: 'GET', path: '/api/v0/status', + method: 'GET', + path: '/api/v0/status', handler: handlers.Status.index, options: { description: 'Simply check to see that the server is online and responding', @@ -70,6 +71,31 @@ server.route({ } }) +const Instructor = Joi.object({ + id: Joi.number().integer().required(), + name: Joi.string().required(), + position: Joi.string().email().optional(), + slug: Joi.string().email().optional(), +}).label('Instructor') + +server.route({ + method: 'GET', + path: '/api/instructors', + handler: handlers.Instructors.index, + options: { + description: 'List instructors at the academy', + tags: ['api', 'instructors'], + response: { + failAction: 'log', + schema: Joi.object({ + instructors: Joi.array() + .items(Instructor) + .label('Instructors') + }) + } + } +}) + var started = false export async function start() { diff --git a/src/server/handlers/instructors.ts b/src/server/handlers/instructors.ts new file mode 100644 index 0000000..1367397 --- /dev/null +++ b/src/server/handlers/instructors.ts @@ -0,0 +1,20 @@ + +import prisma from '../../prisma' + +export async function index(req, h) { + + try { + + const instructors = await prisma.instructor.findMany() + + return h.response({ instructors }).code(200) + + } catch(error) { + + console.log(error) + return h.response(error).code(500) + + } + + } + \ No newline at end of file From 3dbf4e1978708e64b0f90500ffbaf6575a474088 Mon Sep 17 00:00:00 2001 From: My CTO Date: Tue, 5 Dec 2023 21:58:17 +0100 Subject: [PATCH 2/4] Github Workflow For Deployment to Production and Develop --- .github/workflows/main.yml | 92 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..5c10a54 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,92 @@ +name: CI/CD Pipeline + +on: push # Triggers the workflow on push to any branch + +env: + POSTGRES_PRISMA_URL: postgresql://postgres:postgres@localhost:5432/test_db + POSTGRES_URL_NON_POOLING: postgresql://postgres:postgres@localhost:5432/test_db + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db + JWT_SECRET: ${{ secrets.JWT_SECRET }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + +jobs: + build: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:13 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test_db + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '20.6.0' + + - name: Install Dependencies + run: npm install + + - name: Wait for PostgreSQL to start + run: | + for i in {1..10}; do + pg_isready -h localhost -p 5432 -U postgres && echo "PostgreSQL started" && break + echo "Waiting for PostgreSQL to start..." + sleep 5 + done + + - name: Push Prisma Schema to PostgreSQL + run: npx prisma db push + + - name: Build Static App + env: + NEXT_PUBLIC_SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }} + run: yarn run build + + - name: Run Tests + env: + JWT_SECRET: secret + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + POSTGRES_PRISMA_URL: postgresql://postgres:postgres@localhost:5432/test_db + POSTGRES_URL_NON_POOLING: postgresql://postgres:postgres@localhost:5432/test_db + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + UPLOADS_DIR: ${{ secrets.UPLOADS_DIR }} + run: npm run test + + deploy: + if: github.ref == 'refs/heads/main' + needs: build + runs-on: ubuntu-latest + steps: + - name: Setup SSH Agent + uses: webfactory/ssh-agent@v0.5.4 + with: + ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }} + - name: Deploy to Web Servers + run: ssh -o StrictHostKeyChecking=no ubuntu@zencodeschool.com "/opt/zencodeschool.com/deploy_production.sh" + + deploy_develop: + if: github.ref == 'refs/heads/develop' + needs: build + runs-on: ubuntu-latest + + steps: + - name: Setup SSH Agents + uses: webfactory/ssh-agent@v0.5.4 + with: + ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }} + - name: Deploy Develop Branch To Staging Web Server + run: ssh -o StrictHostKeyChecking=no ubuntu@develop.zencodeschool.com "/opt/zencodeschool.com/deploy_develop.sh" From 4191904079f5ad4ad730f160c8251d26b31454a3 Mon Sep 17 00:00:00 2001 From: My CTO Date: Tue, 5 Dec 2023 22:03:05 +0100 Subject: [PATCH 3/4] Remove invalid npm run build step in CI --- .github/workflows/main.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5c10a54..0a02f66 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,11 +47,6 @@ jobs: - name: Push Prisma Schema to PostgreSQL run: npx prisma db push - - name: Build Static App - env: - NEXT_PUBLIC_SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }} - run: yarn run build - - name: Run Tests env: JWT_SECRET: secret From 41bcbc21156999d6ce81acbc1f07fede6481df63 Mon Sep 17 00:00:00 2001 From: My CTO Date: Tue, 5 Dec 2023 23:17:43 +0100 Subject: [PATCH 4/4] Correct test route for swagger /api --- __tests__/swagger_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/swagger_test.ts b/__tests__/swagger_test.ts index e8cea9f..bf0cefb 100644 --- a/__tests__/swagger_test.ts +++ b/__tests__/swagger_test.ts @@ -26,7 +26,7 @@ describe('Swagger Documentation', () => { const response = await server.inject({ method: 'GET', - url: '/' + url: '/api' }) expect(response.statusCode).to.be.equal(200)