From faf95a0759a5147b2930942107816fcfdd8bf117 Mon Sep 17 00:00:00 2001 From: Francis Duvivier Date: Sun, 1 Dec 2024 13:20:15 +0100 Subject: [PATCH 1/2] Fix production setup --- .dockerignore | 3 +- Dockerfile | 24 +++++------ docker-compose-production.yml | 2 +- package-lock.json | 75 +++++++---------------------------- package.json | 22 +++++----- process.json | 6 ++- tsconfig.json | 2 +- 7 files changed, 42 insertions(+), 92 deletions(-) diff --git a/.dockerignore b/.dockerignore index f4b035a..1721ef2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,6 +8,7 @@ # Useless and heavy folders **/build **/dist +data [...] **/.next **/.vercel @@ -16,4 +17,4 @@ # Other useless files in the image .git/ .github/ -[...] \ No newline at end of file +[...] diff --git a/Dockerfile b/Dockerfile index 29e61a8..0d3ab8a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,23 @@ -# Build Stage -FROM node:22-bookworm-slim AS build - -WORKDIR /home/node/app - -COPY package*.json ./ -RUN npm install -COPY . . -RUN npm run build - -# Prod stage FROM node:22-bookworm-slim WORKDIR /home/node/app ENV NODE_ENV=production # Copy only needed files -COPY --chown=node:node --from=build /home/node/app/dist ./dist -COPY --chown=node:node --from=build /home/node/app/public ./public -COPY --chown=node:node --from=build /home/node/app/package*.json ./ +COPY --chown=node:node ./package*.json . RUN npm ci --only=production --ignore-scripts -COPY process.json . +COPY --chown=node:node process.json . RUN mkdir -p /home/node/.pm2 /home/node/app/logs /home/node/app/pids && chown -R node:node /home/node/.pm2 /home/node/app/logs +# db-migrate stuff +COPY --chown=node:node migrations ./migrations +COPY --chown=node:node database.json . + +COPY --chown=node:node ./public ./public +COPY --chown=node:node ./src ./src + USER node EXPOSE 8081 CMD ["./node_modules/pm2/bin/pm2-runtime", "process.json"] diff --git a/docker-compose-production.yml b/docker-compose-production.yml index 39e7787..7b63001 100644 --- a/docker-compose-production.yml +++ b/docker-compose-production.yml @@ -43,4 +43,4 @@ volumes: networks: badgehub_network: name: badgehub_network - external: true \ No newline at end of file + external: true diff --git a/package-lock.json b/package-lock.json index 7a6f5a2..11c7383 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,14 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.17.6", + "@types/pg": "^8.11.5", + "@types/supertest": "^6.0.2", + "@types/swagger-ui-express": "^4.1.6", "db-migrate": "^0.11.14", "db-migrate-pg": "^1.5.2", + "dotenv": "^16.4.5", "express": "^4.21.1", "jose": "^5.9.6", "moment": "^2.30.1", @@ -19,23 +25,17 @@ "pm2": "^5.4.0", "sql-template-tag": "^5.2.1", "swagger-ui-express": "^5.0.0", - "tsoa": "^6.2.1" + "tsoa": "^6.2.1", + "tsx": "^4.19.2", + "typescript": "^5.4.5" }, "devDependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.17.6", - "@types/pg": "^8.11.5", - "@types/supertest": "^6.0.2", - "@types/swagger-ui-express": "^4.1.6", "@vitest/coverage-v8": "^2.1.1", "concurrently": "^8.2.2", - "dotenv": "^16.4.5", "husky": "^9.0.11", "prettier": "^3.2.5", "pretty-quick": "^4.0.0", "supertest": "^7.0.0", - "tsx": "^4.11.2", - "typescript": "^5.4.5", "vite-tsconfig-paths": "^5.1.3", "vitest": "^2.1.1" } @@ -132,7 +132,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -148,7 +147,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -164,7 +162,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -180,7 +177,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -196,7 +192,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -212,7 +207,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -228,7 +222,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -244,7 +237,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -260,7 +252,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -276,7 +267,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -292,7 +282,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -308,7 +297,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -324,7 +312,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -340,7 +327,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -356,7 +342,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -372,7 +357,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -388,7 +372,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -404,7 +387,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -420,7 +402,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -436,7 +417,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -452,7 +432,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -468,7 +447,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -484,7 +462,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -500,7 +477,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1365,8 +1341,7 @@ "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==" }, "node_modules/@types/cookies": { "version": "0.9.0", @@ -1448,8 +1423,7 @@ "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==" }, "node_modules/@types/mime": { "version": "1.3.5", @@ -1476,7 +1450,6 @@ "version": "8.11.10", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.10.tgz", "integrity": "sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==", - "dev": true, "dependencies": { "@types/node": "*", "pg-protocol": "*", @@ -1516,7 +1489,6 @@ "version": "8.1.9", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "dev": true, "dependencies": { "@types/cookiejar": "^2.1.5", "@types/methods": "^1.1.4", @@ -1528,7 +1500,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", - "dev": true, "dependencies": { "@types/methods": "^1.1.4", "@types/superagent": "^8.1.0" @@ -1538,7 +1509,6 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.7.tgz", "integrity": "sha512-ovLM9dNincXkzH4YwyYpll75vhzPBlWx6La89wwvYH7mHjVpf0X0K/vR/aUM7SRxmr5tt9z7E5XJcjQ46q+S3g==", - "dev": true, "dependencies": { "@types/express": "*", "@types/serve-static": "*" @@ -1820,8 +1790,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/atomic-sleep": { "version": "1.0.0", @@ -2196,7 +2165,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2651,7 +2619,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -2687,7 +2654,6 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, "engines": { "node": ">=12" }, @@ -2758,7 +2724,6 @@ "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3128,7 +3093,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3244,7 +3208,6 @@ "version": "4.8.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", - "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -4157,8 +4120,7 @@ "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, "node_modules/on-exit-leak-free": { "version": "2.1.2", @@ -4410,7 +4372,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", - "dev": true, "engines": { "node": ">=4" } @@ -4432,7 +4393,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", - "dev": true, "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", @@ -4772,7 +4732,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", - "dev": true, "engines": { "node": ">=12" } @@ -4781,7 +4740,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "dev": true, "dependencies": { "obuf": "~1.1.2" }, @@ -4793,7 +4751,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", - "dev": true, "engines": { "node": ">=12" } @@ -4802,7 +4759,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "dev": true, "engines": { "node": ">=12" } @@ -4810,8 +4766,7 @@ "node_modules/postgres-range": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", - "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", - "dev": true + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==" }, "node_modules/prettier": { "version": "3.4.1", @@ -5080,7 +5035,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -5862,7 +5816,6 @@ "version": "4.19.2", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", - "dev": true, "dependencies": { "esbuild": "~0.23.0", "get-tsconfig": "^4.7.5" diff --git a/package.json b/package.json index f7fa3e4..866c830 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "1.0.0", "description": "Node project for the BadgeHub API", "type": "module", - "main": "dist/index.js", + "main": "src/index.ts", "scripts": { "install:container": "docker exec -it badgehub-api-node-1 npm install", - "start": "node dist/index.js", + "start": "node --import tsx src/index.ts", "monitor": "docker exec -it badgehub-api-node-1 npx pm2 monit", "lint": "pretty-quick --check", "build": "tsc", @@ -22,8 +22,14 @@ }, "license": "MIT", "dependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.17.6", + "@types/pg": "^8.11.5", + "@types/supertest": "^6.0.2", + "@types/swagger-ui-express": "^4.1.6", "db-migrate": "^0.11.14", "db-migrate-pg": "^1.5.2", + "dotenv": "^16.4.5", "express": "^4.21.1", "jose": "^5.9.6", "moment": "^2.30.1", @@ -32,23 +38,17 @@ "pm2": "^5.4.0", "sql-template-tag": "^5.2.1", "swagger-ui-express": "^5.0.0", - "tsoa": "^6.2.1" + "tsoa": "^6.2.1", + "tsx": "^4.19.2", + "typescript": "^5.4.5" }, "devDependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.17.6", - "@types/pg": "^8.11.5", - "@types/supertest": "^6.0.2", - "@types/swagger-ui-express": "^4.1.6", "@vitest/coverage-v8": "^2.1.1", "concurrently": "^8.2.2", - "dotenv": "^16.4.5", "husky": "^9.0.11", "prettier": "^3.2.5", "pretty-quick": "^4.0.0", "supertest": "^7.0.0", - "tsx": "^4.11.2", - "typescript": "^5.4.5", "vite-tsconfig-paths": "^5.1.3", "vitest": "^2.1.1" } diff --git a/process.json b/process.json index 5f7b5a3..23fd00c 100644 --- a/process.json +++ b/process.json @@ -1,8 +1,10 @@ { - "script": "dist/index.js", + "script": "src/index.ts", + "interpreter": "node", + "interpreter_args": "--import tsx", "name": "badgehub", "exec_mode": "cluster", - "instances": 4, + "instances": 1, "error_file": "./logs/error.log", "out_file": "./logs/access.log", "combine_logs": true diff --git a/tsconfig.json b/tsconfig.json index 277dac7..067379f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,7 @@ "module": "esnext", "moduleResolution": "node", "sourceMap": true, - "outDir": "dist", + "noEmit": true, "lib": ["es2022"], "paths": { "@*": ["./src/*"] From 121811854ee4678b66f54fc5143b426413dbf490 Mon Sep 17 00:00:00 2001 From: Francis Duvivier Date: Tue, 3 Dec 2024 19:42:37 +0100 Subject: [PATCH 2/2] Implement using pg_try_advisory_lock for db-migrations to be able to run mulpile instances again --- package.json | 2 ++ process.json | 2 +- src/db/connectionPool.ts | 10 ++++++++++ src/db/migrations.ts | 37 +++++++++++++++++++++++++------------ 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 866c830..cdf51ca 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "build": "tsc", "swagger": "tsoa spec-and-routes", "dev": "node --import tsx --watch src/index.ts", + "docker-compose-test:up": "docker compose -f compose.test.yml up -d", + "docker-compose-test:down": "docker compose -f compose.test.yml down", "backup": "docker exec -it badgehub-api-db-1 /usr/bin/pg_dump --username badgehub badgehub -f /var/backup/data-backup-`date +\"%Y-%m-%dT%H:%m\"`.sql", "overwrite-mockup-data": "docker exec -it badgehub-api-db-1 /usr/bin/pg_dump --username badgehub --schema badgehub badgehub > mockup-data.sql", "test": "vitest --coverage.enabled true", diff --git a/process.json b/process.json index 23fd00c..63cf077 100644 --- a/process.json +++ b/process.json @@ -4,7 +4,7 @@ "interpreter_args": "--import tsx", "name": "badgehub", "exec_mode": "cluster", - "instances": 1, + "instances": 4, "error_file": "./logs/error.log", "out_file": "./logs/access.log", "combine_logs": true diff --git a/src/db/connectionPool.ts b/src/db/connectionPool.ts index b279744..b732e8c 100644 --- a/src/db/connectionPool.ts +++ b/src/db/connectionPool.ts @@ -20,3 +20,13 @@ export const getPool = () => { } return pool; }; + +export const getClient = () => { + return new pg.Client({ + host: POSTGRES_HOST, + database: POSTGRES_DB, + user: POSTGRES_USER, + password: POSTGRES_PASSWORD, + port: POSTGRES_PORT, + }); +}; diff --git a/src/db/migrations.ts b/src/db/migrations.ts index 3282acc..48dd517 100644 --- a/src/db/migrations.ts +++ b/src/db/migrations.ts @@ -1,19 +1,32 @@ import { exec } from "node:child_process"; +import { getClient, getPool } from "@db/connectionPool"; +import sql from "sql-template-tag"; + +const MIGRATION_LOCK_ID = 108; export async function runMigrations() { // This code runs the npm script 'db-migrate:up' to make sure all migrations are done + console.log(`Waiting for db-migrations lock ${MIGRATION_LOCK_ID}`); + const dbClient = getClient(); + await dbClient.connect(); + await dbClient.query(sql`SELECT pg_advisory_lock(${MIGRATION_LOCK_ID})`); console.log("Running migrations via child process"); - return new Promise((resolve, reject) => { - exec("npm run db-migrate:up", (error, stdout, stderr) => { - stdout && console.log(stdout); - stderr && console.error(stderr); - if (error) { - console.error(`Error running migrations: ${error}`); - reject(error); - } else { - resolve(); - } - console.log("Migrations done"); + try { + return await new Promise((resolve, reject) => { + exec("npm run db-migrate:up", (error, stdout, stderr) => { + stdout && console.log(stdout); + stderr && console.error(stderr); + if (error) { + console.error(`Error running migrations: ${error}`); + reject(error); + } else { + resolve(); + } + console.log("Migrations done"); + }); }); - }); + } finally { + await dbClient.end(); + console.log("released db-migrations"); + } }