From fce7bcb23f7c3a844cb168d07ba64e2c4246227a Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:29:32 +0100 Subject: [PATCH 01/11] fix: update Prisma configuration to use environment variable for DATABASE_URL and add env variable in CI/CD workflow --- .github/workflows/cicd.yml | 4 ++++ prisma.config.ts | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 55a903f..7c00e61 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -36,6 +36,8 @@ jobs: run: npm ci - name: Prisma Generate + env: + DATABASE_URL: postgresql://user:password@localhost:5432/db run: npx prisma generate - name: Run tests @@ -91,6 +93,8 @@ jobs: run: npm ci - name: Prisma Generate + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} run: npx prisma generate - name: Prisma Migrate Deploy diff --git a/prisma.config.ts b/prisma.config.ts index 6c0d76d..f128263 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -1,5 +1,5 @@ import 'dotenv/config'; -import { defineConfig, env } from 'prisma/config'; +import { defineConfig } from 'prisma/config'; export default defineConfig({ schema: 'src/infrastructure/database/schema/schema.prisma', @@ -8,6 +8,8 @@ export default defineConfig({ seed: 'tsx src/infrastructure/database/seed.ts', }, datasource: { - url: env('DATABASE_URL'), + url: + process.env.DATABASE_URL || + 'postgresql://user:password@localhost:5432/db', }, }); From ce17223b0d57499d3a67150bec48bf0d1e830a16 Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:32:27 +0100 Subject: [PATCH 02/11] feat: add CI test script for Jest with coverage and max workers --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0099035..f4849c0 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", + "test:ci": "jest --ci --coverage --maxWorkers=2", "db:generate": "prisma generate", "db:push": "prisma db push", "db:studio": "prisma studio", From bb09304ec72201579789b9d3fda58aadb4d9590e Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:33:53 +0100 Subject: [PATCH 03/11] fix: update Jest configuration for coverage and module resolution --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f4849c0..1fd05de 100644 --- a/package.json +++ b/package.json @@ -74,18 +74,18 @@ "json", "ts" ], - "rootDir": "src", + "rootDir": ".", "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": [ - "**/*.(t|j)s" + "src/**/*.(t|j)s" ], - "coverageDirectory": "../coverage", + "coverageDirectory": "coverage", "testEnvironment": "node", - "prisma": { - "schema": "./src/db/schema.prisma" + "moduleNameMapper": { + "^src/(.*)$": "/src/$1" } } } From 836e6e0a08be312459b5bb07d4ba53c08eabcd9e Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:36:02 +0100 Subject: [PATCH 04/11] Fix CI/CD pipeline issues - Add DATABASE_URL env var to Prisma generate steps in workflow - Update prisma.config.ts to use process.env with fallback - Add test:ci script to package.json - Fix Jest configuration to support @/* path aliases - Update tests to properly mock dependencies - Remove obsolete app.controller.spec.ts test --- package.json | 2 +- test/app.controller.spec.ts | 22 ---------------------- test/user.controller.spec.ts | 14 ++++++++++++++ test/user.service.spec.ts | 18 +++++++++++++++++- 4 files changed, 32 insertions(+), 24 deletions(-) delete mode 100644 test/app.controller.spec.ts diff --git a/package.json b/package.json index 1fd05de..002bdb7 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "coverageDirectory": "coverage", "testEnvironment": "node", "moduleNameMapper": { - "^src/(.*)$": "/src/$1" + "^@/(.*)$": "/src/$1" } } } diff --git a/test/app.controller.spec.ts b/test/app.controller.spec.ts deleted file mode 100644 index d22f389..0000000 --- a/test/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); -}); diff --git a/test/user.controller.spec.ts b/test/user.controller.spec.ts index 68623bb..2a2218d 100644 --- a/test/user.controller.spec.ts +++ b/test/user.controller.spec.ts @@ -1,5 +1,13 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UserController } from '../src/presentation/controllers/user.controller'; +import { IUserService } from '../src/application/ports/user.port'; + +const mockUserService: Partial = { + getAll: jest.fn(), + getById: jest.fn(), + update: jest.fn(), + delete: jest.fn(), +}; describe('UserController', () => { let controller: UserController; @@ -7,6 +15,12 @@ describe('UserController', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [UserController], + providers: [ + { + provide: 'SERVICE.USER', + useValue: mockUserService, + }, + ], }).compile(); controller = module.get(UserController); diff --git a/test/user.service.spec.ts b/test/user.service.spec.ts index 547f4d2..5044322 100644 --- a/test/user.service.spec.ts +++ b/test/user.service.spec.ts @@ -1,12 +1,28 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UserService } from '../src/application/services/user.service'; +import { IUserRepository } from '../src/domain/user/user-repository.interface'; + +const mockUserRepository: Partial = { + findAll: jest.fn(), + findById: jest.fn(), + findByEmail: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), +}; describe('UserService', () => { let service: UserService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [UserService], + providers: [ + UserService, + { + provide: 'REPO.USER', + useValue: mockUserRepository, + }, + ], }).compile(); service = module.get(UserService); From 418e1507db3b89ad9d5032f8768e0a942001f32c Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:37:44 +0100 Subject: [PATCH 05/11] fix: update datasource URL to require DATABASE_URL environment variable --- prisma.config.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prisma.config.ts b/prisma.config.ts index f128263..578593b 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -8,8 +8,6 @@ export default defineConfig({ seed: 'tsx src/infrastructure/database/seed.ts', }, datasource: { - url: - process.env.DATABASE_URL || - 'postgresql://user:password@localhost:5432/db', + url: process.env.DATABASE_URL!, }, }); From d0723c8fe0a34ba6ced6857591808415b838f54f Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:38:31 +0100 Subject: [PATCH 06/11] fix: ensure DATABASE_URL is set before using it in datasource configuration --- prisma.config.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/prisma.config.ts b/prisma.config.ts index 578593b..16225fb 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -8,6 +8,11 @@ export default defineConfig({ seed: 'tsx src/infrastructure/database/seed.ts', }, datasource: { - url: process.env.DATABASE_URL!, + url: (() => { + if (!process.env.DATABASE_URL) { + throw new Error('DATABASE_URL environment variable must be set'); + } + return process.env.DATABASE_URL; + })(), }, }); From a97c2e927dfa3d7ae40802639c7806bc679d2f73 Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:39:56 +0100 Subject: [PATCH 07/11] fix: update Prisma Generate step to use DATABASE_URL from secrets --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 7c00e61..7c5938e 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -37,7 +37,7 @@ jobs: - name: Prisma Generate env: - DATABASE_URL: postgresql://user:password@localhost:5432/db + DATABASE_URL: ${{ secrets.DATABASE_URL }} run: npx prisma generate - name: Run tests From 395135a96fdf236afaa05793770626811f0f0c38 Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:42:57 +0100 Subject: [PATCH 08/11] fix: remove package-lock.json from .dockerignore --- .dockerignore | 1 - package-lock.json | 182 ++++++++++++++++++++++++---------------------- 2 files changed, 95 insertions(+), 88 deletions(-) diff --git a/.dockerignore b/.dockerignore index f239db6..5de0f55 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,7 +2,6 @@ node_modules npm-debug.log yarn-error.log -package-lock.json yarn.lock # Build output diff --git a/package-lock.json b/package-lock.json index 0804ad1..6c4fa62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3014,9 +3014,9 @@ } }, "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, "node_modules/@tokenizer/inflate": { @@ -3403,17 +3403,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", - "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz", + "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.49.0", - "@typescript-eslint/type-utils": "8.49.0", - "@typescript-eslint/utils": "8.49.0", - "@typescript-eslint/visitor-keys": "8.49.0", + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/type-utils": "8.50.0", + "@typescript-eslint/utils": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" @@ -3426,7 +3426,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.49.0", + "@typescript-eslint/parser": "^8.50.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3442,16 +3442,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", - "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.0.tgz", + "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.49.0", - "@typescript-eslint/types": "8.49.0", - "@typescript-eslint/typescript-estree": "8.49.0", - "@typescript-eslint/visitor-keys": "8.49.0", + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4" }, "engines": { @@ -3467,14 +3467,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", - "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz", + "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.49.0", - "@typescript-eslint/types": "^8.49.0", + "@typescript-eslint/tsconfig-utils": "^8.50.0", + "@typescript-eslint/types": "^8.50.0", "debug": "^4.3.4" }, "engines": { @@ -3489,14 +3489,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", - "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz", + "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.49.0", - "@typescript-eslint/visitor-keys": "8.49.0" + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3507,9 +3507,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", - "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz", + "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==", "dev": true, "license": "MIT", "engines": { @@ -3524,15 +3524,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", - "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.0.tgz", + "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.49.0", - "@typescript-eslint/typescript-estree": "8.49.0", - "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/utils": "8.50.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3549,9 +3549,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", - "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz", + "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==", "dev": true, "license": "MIT", "engines": { @@ -3563,16 +3563,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", - "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz", + "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.49.0", - "@typescript-eslint/tsconfig-utils": "8.49.0", - "@typescript-eslint/types": "8.49.0", - "@typescript-eslint/visitor-keys": "8.49.0", + "@typescript-eslint/project-service": "8.50.0", + "@typescript-eslint/tsconfig-utils": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -3617,16 +3617,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", - "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.0.tgz", + "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.49.0", - "@typescript-eslint/types": "8.49.0", - "@typescript-eslint/typescript-estree": "8.49.0" + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3641,13 +3641,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", - "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz", + "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/types": "8.50.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5184,21 +5184,12 @@ "node": ">= 0.8.0" } }, - "node_modules/cookie-parser/node_modules/cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", @@ -5998,6 +5989,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -8466,9 +8466,9 @@ } }, "node_modules/named-placeholders": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.4.tgz", - "integrity": "sha512-/qfG0Kk/bLJIvej4FcPQ2KYUJP8iQdU1CTxysNb/U2wUNb+/4K485yeio8iNoiwfqJnsTInXoRPTza0dZWHVJQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", "license": "MIT", "dependencies": { "lru.min": "^1.1.0" @@ -9510,25 +9510,29 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/seq-queue": { @@ -9547,9 +9551,9 @@ } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -9559,6 +9563,10 @@ }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -10554,16 +10562,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", - "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.0.tgz", + "integrity": "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.49.0", - "@typescript-eslint/parser": "8.49.0", - "@typescript-eslint/typescript-estree": "8.49.0", - "@typescript-eslint/utils": "8.49.0" + "@typescript-eslint/eslint-plugin": "8.50.0", + "@typescript-eslint/parser": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/utils": "8.50.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" From 4d901efec890fb888b5130a149d2c0036a99047b Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 13:45:13 +0100 Subject: [PATCH 09/11] fix: move DATABASE_URL environment variable declaration to the correct location in Dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index d8c2195..807258a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ RUN npm ci COPY . . # Generate Prisma client +ENV DATABASE_URL="postgresql://user:password@localhost:5432/db" RUN npm run db:generate # Build application From 726e9fe9791781c481e9cecf38741caf1a72359c Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 14:44:18 +0100 Subject: [PATCH 10/11] fix: update DATABASE_URL in .env.example and Dockerfile for MySQL connection --- .env.example | 4 +++- .github/workflows/cicd.yml | 33 +++++++++++++++++++++++++++++++-- Dockerfile | 4 ++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 7b00024..d5b75e5 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,9 @@ PUBLIC_URL="http://localhost:4000" PUBLIC_PORT=4000 ALLOWED_ORIGINS=http://localhost:5173 +# Set a strong random value for `SESSION_SECRET` in your local `.env` SESSION_SECRET="" -# Set a strong random value for `SESSION_SECRET` in your local `.env` +# For MySQL (dev): mysql://user:password@localhost:3306/database +# For Azure MySQL: mysql://user@server:password@server.mysql.database.azure.net:3306/database DATABASE_URL="" \ No newline at end of file diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 7c5938e..f471aff 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -80,10 +80,28 @@ jobs: name: Prisma Migrate Deploy runs-on: ubuntu-latest needs: docker_build_push - + # Uncomment when Azure MySQL database is created and DATABASE_URL secret is set steps: - uses: actions/checkout@v3 + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Get Runner IP + id: ip + run: echo "runner_ip=$(curl -s https://api.ipify.org)" >> $GITHUB_OUTPUT + + - name: Add Runner IP to MySQL Firewall + run: | + az mysql flexible-server firewall-rule create \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --name ${{ secrets.AZURE_MYSQL_SERVER_NAME }} \ + --rule-name "GitHub-Actions-${{ github.run_id }}" \ + --start-ip-address ${{ steps.ip.outputs.runner_ip }} \ + --end-ip-address ${{ steps.ip.outputs.runner_ip }} + - name: Setup Node.js uses: actions/setup-node@v3 with: @@ -102,10 +120,21 @@ jobs: DATABASE_URL: ${{ secrets.DATABASE_URL }} run: npx prisma migrate deploy + - name: Remove Runner IP from MySQL Firewall + if: always() + run: | + az mysql flexible-server firewall-rule delete \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --name ${{ secrets.AZURE_MYSQL_SERVER_NAME }} \ + --rule-name "GitHub-Actions-${{ github.run_id }}" \ + --yes + deploy: name: Deploy to Azure Container App runs-on: ubuntu-latest - needs: prisma_migrate + needs: docker_build_push + # Deploy after docker build, migration runs in parallel if configured + if: always() && needs.docker_build_push.result == 'success' steps: - name: Azure Login diff --git a/Dockerfile b/Dockerfile index 807258a..4445fec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,8 +13,8 @@ RUN npm ci # Copy source files COPY . . -# Generate Prisma client -ENV DATABASE_URL="postgresql://user:password@localhost:5432/db" +# Generate Prisma client (dummy MySQL connection for build) +ENV DATABASE_URL="mysql://user:password@localhost:3306/db" RUN npm run db:generate # Build application From 342442f2707d60f364c390d0a0180b3e31f5a3b8 Mon Sep 17 00:00:00 2001 From: Daniel van Ginneken Date: Tue, 16 Dec 2025 14:47:52 +0100 Subject: [PATCH 11/11] fix: only deploy on push to main, not on pull requests --- .github/workflows/cicd.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index f471aff..856faa7 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -50,6 +50,7 @@ jobs: name: Build & Push Docker Image runs-on: ubuntu-latest needs: build_test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 @@ -80,7 +81,8 @@ jobs: name: Prisma Migrate Deploy runs-on: ubuntu-latest needs: docker_build_push - # Uncomment when Azure MySQL database is created and DATABASE_URL secret is set + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: - uses: actions/checkout@v3 @@ -132,9 +134,8 @@ jobs: deploy: name: Deploy to Azure Container App runs-on: ubuntu-latest - needs: docker_build_push - # Deploy after docker build, migration runs in parallel if configured - if: always() && needs.docker_build_push.result == 'success' + needs: [docker_build_push, prisma_migrate] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.docker_build_push.result == 'success' steps: - name: Azure Login