From 20b2eee5d6923201a880e8c6181b36b70b8a2751 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Wed, 13 Dec 2023 17:59:21 +0100 Subject: [PATCH 01/19] Pin Node 18.18 --- apps/web/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index c8d073166..0a413dd06 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18 as pnpm +FROM node:18.18 as pnpm ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" From 5d9be847b14a8d35dc6501da01501eeb414f0158 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Wed, 13 Dec 2023 17:59:33 +0100 Subject: [PATCH 02/19] Bump submodule --- apps/duck-estimator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/duck-estimator b/apps/duck-estimator index 46ca02b77..b1a1271c0 160000 --- a/apps/duck-estimator +++ b/apps/duck-estimator @@ -1 +1 @@ -Subproject commit 46ca02b7760391de17fad9ec9b863357311491d0 +Subproject commit b1a1271c0585ce820e12b65dfa359109452375df From 85d3b5566d3a14ba7a11e9db28c1cdbdf04cb90b Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Thu, 14 Dec 2023 21:43:39 +0100 Subject: [PATCH 03/19] Fix workflow Bump submodules --- .github/workflows/daily.yml | 2 +- apps/duck-estimator | 2 +- apps/duckguessr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index 8bcbf9f17..e0254d04a 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -54,7 +54,7 @@ jobs: key: ${{ secrets.PRODUCTION_SSH_KEY }} command_timeout: 60m script: | - cd workspace/DucksManager2 + cd workspace/DucksManager2/apps/duck-estimator docker compose run --rm --no-deps duck-estimator - name: Run sprite-names-updater diff --git a/apps/duck-estimator b/apps/duck-estimator index b1a1271c0..612e1cf79 160000 --- a/apps/duck-estimator +++ b/apps/duck-estimator @@ -1 +1 @@ -Subproject commit b1a1271c0585ce820e12b65dfa359109452375df +Subproject commit 612e1cf79d5761bc515221b0c892e0b42fd4d131 diff --git a/apps/duckguessr b/apps/duckguessr index fc58de7b7..323e72c8f 160000 --- a/apps/duckguessr +++ b/apps/duckguessr @@ -1 +1 @@ -Subproject commit fc58de7b7495adb8fcd77f74d5491ab47b2b5e22 +Subproject commit 323e72c8f1ec3c6cb951e0cd0c4e8631d9e3a602 From 7534326fdba3d70d8cb052ee64555f03ad6a5955 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Wed, 6 Dec 2023 00:20:32 +0100 Subject: [PATCH 04/19] Init send-pending-emails.ts --- packages/api/scripts/send-pending-emails.ts | 84 +++++++++++++++++++ packages/prisma-clients/.npmrc | 2 + packages/prisma-clients/pnpm-lock.yaml | 10 +-- .../prisma-clients/schemas/schema_dm.prisma | 2 +- 4 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 packages/api/scripts/send-pending-emails.ts create mode 100644 packages/prisma-clients/.npmrc diff --git a/packages/api/scripts/send-pending-emails.ts b/packages/api/scripts/send-pending-emails.ts new file mode 100644 index 000000000..7721a378b --- /dev/null +++ b/packages/api/scripts/send-pending-emails.ts @@ -0,0 +1,84 @@ +import dotenv from "dotenv"; + +dotenv.config({ + path: "../.env", +}); + +import BookstoreApproved from "~/emails/bookstore-approved"; +import EdgesPublishedWithCreator from "~/emails/edges-published-with-creator"; +import EdgesPublishedWithPhotographer from "~/emails/edges-published-with-photographer"; +import { PrismaClient, userContribution, userContributionType } from "~prisma-clients/client_dm"; +const prismaDmClient = new PrismaClient(); +const medalLevels = { + [userContributionType.photographe]: { 1: 50, 2: 150, 3: 600 }, + [userContributionType.createur]: { 1: 20, 2: 70, 3: 150 }, + [userContributionType.duckhunter]: { 1: 1, 2: 3, 3: 5 } +} as const; + + +const emailsSent = []; +for (const contributionTypeStr in medalLevels) { + const contributionType = contributionTypeStr as userContributionType; + const pendingEmailContributionsForType = await prismaDmClient.userContribution.findMany({ + where: { + contribution: contributionType, + isEmailSent: false + }, orderBy: { + userId: 'asc', + totalPoints: 'asc' + }}); + if (!pendingEmailContributionsForType.length) { + console.info(`No email to send for contribution ${contributionType}`); + } else { + const pendingEmailContributionsByUser = pendingEmailContributionsForType.reduce>((acc, contribution) => ({ + ...acc, [contribution.userId]: [...((acc[contribution.userId]) || []), contribution] + }), {}); + + for (const [userId, pendingEmailContributionsForUser] of Object.entries(pendingEmailContributionsByUser)) { + console.info(`${pendingEmailContributionsForUser.length} contributions pending for user ${userId}`); + const initialPointsCount = pendingEmailContributionsForUser[0].totalPoints - pendingEmailContributionsForUser[0].newPoints; + const finalPointsCount = pendingEmailContributionsForUser[pendingEmailContributionsForUser.length - 1].totalPoints; + const pointsEarned = finalPointsCount - initialPointsCount; + + const medalReached = Object.entries(medalLevels[contributionType]).reduce((medalReached, [medal, medalThreshold]) => + (initialPointsCount < medalThreshold && finalPointsCount >= medalThreshold) ? parseInt(medal): medalReached, null as number|null) + + await prismaDmClient.$transaction( + pendingEmailContributionsForUser.map(({ id }) => + prismaDmClient.userContribution.update({ + where: { + id, + }, + data: { + isEmailSent: true + }, + }) + ) + ); + + const user = await prismaDmClient.user.findUniqueOrThrow({ where: {id: parseInt(userId)}}); + + const locale = 'fr'; + let email; + switch (contributionType) { + case 'duckhunter': + email =(await new BookstoreApproved({ user, locale, newMedalLevel: medalReached })); + break; + case 'photographe': + email = (await new EdgesPublishedWithPhotographer({ user, locale, extraEdges: pendingEmailContributionsForUser.length, extraPhotographerPoints: pointsEarned, newMedalLevel: medalReached })); + break; + case 'createur': + email = (await new EdgesPublishedWithCreator({ user, locale, extraEdges: pendingEmailContributionsForUser.length, extraCreatorPoints: pointsEarned, newMedalLevel: medalReached })); + break; + } + emailsSent.push(email) + email.send(); + } + } +} +console.log(JSON.stringify({ + 'emails_sent': emailsSent.map(emailHelper => ({ + 'to': emailHelper.getTo(), + 'subject': emailHelper.getSubject() + })) +})); diff --git a/packages/prisma-clients/.npmrc b/packages/prisma-clients/.npmrc new file mode 100644 index 000000000..f98d3bc8f --- /dev/null +++ b/packages/prisma-clients/.npmrc @@ -0,0 +1,2 @@ +strict-peer-dependencies=false +shared-workspace-lockfile=false diff --git a/packages/prisma-clients/pnpm-lock.yaml b/packages/prisma-clients/pnpm-lock.yaml index 239671427..771ed8e4a 100644 --- a/packages/prisma-clients/pnpm-lock.yaml +++ b/packages/prisma-clients/pnpm-lock.yaml @@ -15,7 +15,7 @@ dependencies: devDependencies: ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.10.0)(typescript@5.3.2) + version: 10.9.1(@types/node@20.10.3)(typescript@5.3.2) packages: @@ -81,8 +81,8 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node@20.10.0: - resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==} + /@types/node@20.10.3: + resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==} dependencies: undici-types: 5.26.5 dev: true @@ -124,7 +124,7 @@ packages: '@prisma/engines': 5.6.0 dev: false - /ts-node@10.9.1(@types/node@20.10.0)(typescript@5.3.2): + /ts-node@10.9.1(@types/node@20.10.3)(typescript@5.3.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -143,7 +143,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.10.0 + '@types/node': 20.10.3 acorn: 8.11.2 acorn-walk: 8.3.0 arg: 4.1.3 diff --git a/packages/prisma-clients/schemas/schema_dm.prisma b/packages/prisma-clients/schemas/schema_dm.prisma index 5129628cd..5620929ba 100644 --- a/packages/prisma-clients/schemas/schema_dm.prisma +++ b/packages/prisma-clients/schemas/schema_dm.prisma @@ -1,7 +1,7 @@ generator client { provider = "prisma-client-js" output = "../client_dm" - binaryTargets = ["native", "debian-openssl-1.1.x", "debian-openssl-3.0.x"] + binaryTargets = ["native", "debian-openssl-1.1.x", "debian-openssl-3.0.x", "darwin-arm64"] } datasource db { From 6a60e84675c367bbb9387bc1c8a9570f2e48c8bf Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Thu, 14 Dec 2023 22:01:12 +0100 Subject: [PATCH 05/19] Various fixes --- .github/workflows/daily.yml | 4 +- .github/workflows/deploy-duck-estimator.yml | 66 +++++++++++++++++++++ .gitmodules | 11 +++- packages/api/translations/messages.en.json | 6 +- pnpm-workspace.yaml | 8 ++- 5 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/deploy-duck-estimator.yml diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index e0254d04a..879bcaad0 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -55,7 +55,9 @@ jobs: command_timeout: 60m script: | cd workspace/DucksManager2/apps/duck-estimator - docker compose run --rm --no-deps duck-estimator + echo ${{ secrets.DOCKER_REGISTRY_TOKEN_DM }} | docker login ghcr.io -u bperel --password-stdin + docker compose pull + docker compose run --rm duck-estimator - name: Run sprite-names-updater uses: appleboy/ssh-action@v0.1.4 diff --git a/.github/workflows/deploy-duck-estimator.yml b/.github/workflows/deploy-duck-estimator.yml new file mode 100644 index 000000000..725f3efec --- /dev/null +++ b/.github/workflows/deploy-duck-estimator.yml @@ -0,0 +1,66 @@ +name: deploy-duck-estimator + +on: + workflow_dispatch: + push: + paths: + - ".github/workflows/deploy-duck-estimator.yml" + - "apps/duck-estimator" + - "pnpm-lock.yaml" + branches: + - "master" +jobs: + deploy-duck-estimator: + runs-on: ubuntu-latest + environment: production + steps: + - name: Check out repository + uses: actions/checkout@master + with: + submodules: recursive + + - name: Download web .env file + uses: nicklasfrahm/scp-action@main + with: + direction: download + host: ${{ secrets.PRODUCTION_SSH_HOST }} + fingerprint: ${{ secrets.PRODUCTION_SSH_FINGERPRINT }} + username: ${{ secrets.PRODUCTION_SSH_USER }} + key: ${{ secrets.PRODUCTION_SSH_KEY }} + source: /home/bperel/workspace/DucksManager2/apps/duck-estimator/.env.local + target: apps/duck-estimator/.env.local + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:v0.12.3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: bperel + password: ${{ secrets.DOCKER_REGISTRY_TOKEN_DM }} + + - name: Build and push app + uses: docker/build-push-action@v5 + with: + context: . + file: apps/duck-estimator/Dockerfile + platforms: linux/x86_64 + push: true + cache-from: type=registry,ref=ghcr.io/bperel/duck-estimator:buildcache + cache-to: type=registry,ref=ghcr.io/bperel/duck-estimator:buildcache,mode=max + tags: ghcr.io/bperel/duck-estimator:latest + + - name: Upload docker-compose.yml file + uses: nicklasfrahm/scp-action@v1.0.1 + with: + direction: upload + host: ${{ secrets.PRODUCTION_SSH_HOST }} + fingerprint: ${{ secrets.PRODUCTION_SSH_FINGERPRINT }} + username: ${{ secrets.PRODUCTION_SSH_USER }} + key: ${{ secrets.PRODUCTION_SSH_KEY }} + source: apps/duck-estimator/docker-compose.yml + target: /home/bperel/workspace/DucksManager2/apps/duck-estimator/docker-compose.yml diff --git a/.gitmodules b/.gitmodules index e22710e8d..955b383c2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,15 @@ path = apps/duck-estimator url = git@github.com:bperel/duck-estimator.git branch=master -[submodule "apps/duckguessr"] +[submodule "duckguessr-old"] + path = apps/duckguessr-old + url = git@github.com:bperel/duckguessr.git + branch=master +[submodule "duckguessr"] path = apps/duckguessr url = git@github.com:bperel/duckguessr.git + branch=nuxt3-to-vite-vue3 +[submodule "duckguessr-ml"] + branch=ts + path = packages/duckguessr-ml + url = git@github.com:bperel/duckguessr-ml.git diff --git a/packages/api/translations/messages.en.json b/packages/api/translations/messages.en.json index 8d0db5406..d8836f0f8 100644 --- a/packages/api/translations/messages.en.json +++ b/packages/api/translations/messages.en.json @@ -17,8 +17,8 @@ "Un magazine faisant partie de vos abonnements DucksManager est récemment sorti et a donc été ajouté automatiquement à votre collection : {{publicationName}} {{issuenumber}}.": "A publication that you are subscribed to has recently released an issue which has been automatically added to your DucksManager collection : {{publicationName}} {{issuenumber}}.", "Un visiteur a indiqué avoir oublié le mot de passe associé à l'adresse e-mail {{email}}.": "A visitor reported that they forgot the password associated with the email address {{email}}.", "Vos tranches ont été publiées sur DucksManager !": "Your edges have been published on DucksManager!", - "Votre contribution vous a rapporté {{{{extraCreatorPoints}}}} points \"Créateur\"": "Your contribution earned you {{{{extraCreatorPoints}}}} \"Creator\" points", - "Votre contribution vous a rapporté {{{{extraPhotographerPoints}}}} points \"Photographe\"": "Your contribution earned you {{{{extraPhotographerPoints}}}} \"Photographer\" points", + "Votre contribution vous a rapporté {{extraCreatorPoints}} points \"Créateur\"": "Your contribution earned you {{extraCreatorPoints}} \"Creator\" points", + "Votre contribution vous a rapporté {{extraPhotographerPoints}} points \"Photographe\"": "Your contribution earned you {{extraPhotographerPoints}} \"Photographer\" points", "Votre phrase de présentation a été acceptée !": "Your presentation sentence has been approved!", "Votre phrase de présentation a été refusée.": "Your presentation sentence has been refused.", "Votre revue de bouquinerie a été approuvée !": "Your bookstore review has been approved!", @@ -32,4 +32,4 @@ "{{publicationName}} {{issuenumber}} a été ajouté à votre collection !": "{{publicationName}} {{issuenumber}} has been added to your collection!", "• 1 nouvelle histoire de {{authorName}}": "• 1 new story from {{authorName}}", "• {{newStoriesNumber}} nouvelles histoires de {{authorName}}": "• {{newStoriesNumber}} new stories from {{authorName}}" -} +} \ No newline at end of file diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2de1661b7..58f3c750e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,6 @@ packages: - - 'apps/*' - - 'apps/*/api' - - 'packages/*' \ No newline at end of file + - "apps/*" + - "!apps/duckguessr-old" + - "apps/*/packages/*" + - "apps/*/api" + - "packages/*" From 4fec48aef9318760de8b65f721ad19e0fa55b782 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Thu, 14 Dec 2023 22:05:39 +0100 Subject: [PATCH 06/19] Update Prisma Watch API folder to build DM --- .github/workflows/deploy-ducksmanager.yml | 1 + packages/api/package.json | 4 +- packages/api/pnpm-lock.yaml | 50 +++++++++++++++------- packages/prisma-clients/package.json | 4 +- packages/prisma-clients/pnpm-lock.yaml | 52 ++++++++++++++++------- 5 files changed, 76 insertions(+), 35 deletions(-) diff --git a/.github/workflows/deploy-ducksmanager.yml b/.github/workflows/deploy-ducksmanager.yml index 3aa73ca84..ce34a9a96 100644 --- a/.github/workflows/deploy-ducksmanager.yml +++ b/.github/workflows/deploy-ducksmanager.yml @@ -6,6 +6,7 @@ on: paths: - ".github/workflows/deploy-ducksmanager.yml" - "apps/web/**" + - "packages/api/**" - "pnpm-lock.yaml" branches: - "master" diff --git a/packages/api/package.json b/packages/api/package.json index 5a48d7741..6803fac91 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -13,7 +13,7 @@ "~prisma-clients": "../prisma-clients" }, "dependencies": { - "@prisma/client": "^5.6.0", + "@prisma/client": "^5.7.0", "@pusher/push-notifications-server": "^1.2.6", "@sentry/node": "^7.81.1", "axios": "^1.6.2", @@ -61,7 +61,7 @@ "eslint-plugin-prettier-vue": "^5.0.0", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-unused-imports": "^3.0.0", - "prisma": "^5.6.0", + "prisma": "^5.7.0", "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", "typescript": "^5.3.2" diff --git a/packages/api/pnpm-lock.yaml b/packages/api/pnpm-lock.yaml index 779084cf0..4d94df28e 100644 --- a/packages/api/pnpm-lock.yaml +++ b/packages/api/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@prisma/client': - specifier: ^5.6.0 - version: 5.6.0(prisma@5.6.0) + specifier: ^5.7.0 + version: 5.7.0(prisma@5.7.0) '@pusher/push-notifications-server': specifier: ^1.2.6 version: 1.2.6 @@ -146,8 +146,8 @@ devDependencies: specifier: ^3.0.0 version: 3.0.0(@typescript-eslint/eslint-plugin@6.12.0)(eslint@8.54.0) prisma: - specifier: ^5.6.0 - version: 5.6.0 + specifier: ^5.7.0 + version: 5.7.0 tsc-alias: specifier: ^1.8.8 version: 1.8.8 @@ -342,8 +342,8 @@ packages: tslib: 2.6.2 dev: true - /@prisma/client@5.6.0(prisma@5.6.0): - resolution: {integrity: sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==} + /@prisma/client@5.7.0(prisma@5.7.0): + resolution: {integrity: sha512-cZmglCrfNbYpzUtz7HscVHl38e9CrUs31nrVoGUK1nIPXGgt8hT4jj2s657UXcNdQ/jBUxDgGmHyu2Nyrq1txg==} engines: {node: '>=16.13'} requiresBuild: true peerDependencies: @@ -352,17 +352,35 @@ packages: prisma: optional: true dependencies: - '@prisma/engines-version': 5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee - prisma: 5.6.0 + prisma: 5.7.0 dev: false - /@prisma/engines-version@5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee: - resolution: {integrity: sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==} - dev: false + /@prisma/debug@5.7.0: + resolution: {integrity: sha512-tZ+MOjWlVvz1kOEhNYMa4QUGURY+kgOUBqLHYIV8jmCsMuvA1tWcn7qtIMLzYWCbDcQT4ZS8xDgK0R2gl6/0wA==} + + /@prisma/engines-version@5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9: + resolution: {integrity: sha512-V6tgRVi62jRwTm0Hglky3Scwjr/AKFBFtS+MdbsBr7UOuiu1TKLPc6xfPiyEN1+bYqjEtjxwGsHgahcJsd1rNg==} - /@prisma/engines@5.6.0: - resolution: {integrity: sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==} + /@prisma/engines@5.7.0: + resolution: {integrity: sha512-TkOMgMm60n5YgEKPn9erIvFX2/QuWnl3GBo6yTRyZKk5O5KQertXiNnrYgSLy0SpsKmhovEPQb+D4l0SzyE7XA==} requiresBuild: true + dependencies: + '@prisma/debug': 5.7.0 + '@prisma/engines-version': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 + '@prisma/fetch-engine': 5.7.0 + '@prisma/get-platform': 5.7.0 + + /@prisma/fetch-engine@5.7.0: + resolution: {integrity: sha512-zIn/qmO+N/3FYe7/L9o+yZseIU8ivh4NdPKSkQRIHfg2QVTVMnbhGoTcecbxfVubeTp+DjcbjS0H9fCuM4W04w==} + dependencies: + '@prisma/debug': 5.7.0 + '@prisma/engines-version': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 + '@prisma/get-platform': 5.7.0 + + /@prisma/get-platform@5.7.0: + resolution: {integrity: sha512-ZeV/Op4bZsWXuw5Tg05WwRI8BlKiRFhsixPcAM+5BKYSiUZiMKIi713tfT3drBq8+T0E1arNZgYSA9QYcglWNA==} + dependencies: + '@prisma/debug': 5.7.0 /@pusher/push-notifications-server@1.2.6: resolution: {integrity: sha512-BbGmD0YzxSvW2CSTYD1sdDT/zCEuhGIeKuTd6T989sQf1e7UhKY+acLuDvnviM/EBcRxxjGZManOS/9+1Gjyww==} @@ -2581,13 +2599,13 @@ packages: hasBin: true dev: true - /prisma@5.6.0: - resolution: {integrity: sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==} + /prisma@5.7.0: + resolution: {integrity: sha512-0rcfXO2ErmGAtxnuTNHQT9ztL0zZheQjOI/VNJzdq87C3TlGPQtMqtM+KCwU6XtmkoEr7vbCQqA7HF9IY0ST+Q==} engines: {node: '>=16.13'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 5.6.0 + '@prisma/engines': 5.7.0 /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} diff --git a/packages/prisma-clients/package.json b/packages/prisma-clients/package.json index d605f1936..04cb41ca6 100644 --- a/packages/prisma-clients/package.json +++ b/packages/prisma-clients/package.json @@ -11,8 +11,8 @@ "build": "pnpm run generate && ./node_modules/.pnpm/node_modules/.bin/tsc" }, "dependencies": { - "@prisma/client": "^5.6.0", - "prisma": "^5.6.0" + "@prisma/client": "^5.7.0", + "prisma": "^5.7.0" }, "devDependencies": { "ts-node": "^10.9.1" diff --git a/packages/prisma-clients/pnpm-lock.yaml b/packages/prisma-clients/pnpm-lock.yaml index 771ed8e4a..ae7a94c97 100644 --- a/packages/prisma-clients/pnpm-lock.yaml +++ b/packages/prisma-clients/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: '@prisma/client': - specifier: ^5.6.0 - version: 5.6.0(prisma@5.6.0) + specifier: ^5.7.0 + version: 5.7.0(prisma@5.7.0) prisma: - specifier: ^5.6.0 - version: 5.6.0 + specifier: ^5.7.0 + version: 5.7.0 devDependencies: ts-node: @@ -42,8 +42,8 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@prisma/client@5.6.0(prisma@5.6.0): - resolution: {integrity: sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==} + /@prisma/client@5.7.0(prisma@5.7.0): + resolution: {integrity: sha512-cZmglCrfNbYpzUtz7HscVHl38e9CrUs31nrVoGUK1nIPXGgt8hT4jj2s657UXcNdQ/jBUxDgGmHyu2Nyrq1txg==} engines: {node: '>=16.13'} requiresBuild: true peerDependencies: @@ -52,17 +52,39 @@ packages: prisma: optional: true dependencies: - '@prisma/engines-version': 5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee - prisma: 5.6.0 + prisma: 5.7.0 dev: false - /@prisma/engines-version@5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee: - resolution: {integrity: sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==} + /@prisma/debug@5.7.0: + resolution: {integrity: sha512-tZ+MOjWlVvz1kOEhNYMa4QUGURY+kgOUBqLHYIV8jmCsMuvA1tWcn7qtIMLzYWCbDcQT4ZS8xDgK0R2gl6/0wA==} dev: false - /@prisma/engines@5.6.0: - resolution: {integrity: sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==} + /@prisma/engines-version@5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9: + resolution: {integrity: sha512-V6tgRVi62jRwTm0Hglky3Scwjr/AKFBFtS+MdbsBr7UOuiu1TKLPc6xfPiyEN1+bYqjEtjxwGsHgahcJsd1rNg==} + dev: false + + /@prisma/engines@5.7.0: + resolution: {integrity: sha512-TkOMgMm60n5YgEKPn9erIvFX2/QuWnl3GBo6yTRyZKk5O5KQertXiNnrYgSLy0SpsKmhovEPQb+D4l0SzyE7XA==} requiresBuild: true + dependencies: + '@prisma/debug': 5.7.0 + '@prisma/engines-version': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 + '@prisma/fetch-engine': 5.7.0 + '@prisma/get-platform': 5.7.0 + dev: false + + /@prisma/fetch-engine@5.7.0: + resolution: {integrity: sha512-zIn/qmO+N/3FYe7/L9o+yZseIU8ivh4NdPKSkQRIHfg2QVTVMnbhGoTcecbxfVubeTp+DjcbjS0H9fCuM4W04w==} + dependencies: + '@prisma/debug': 5.7.0 + '@prisma/engines-version': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 + '@prisma/get-platform': 5.7.0 + dev: false + + /@prisma/get-platform@5.7.0: + resolution: {integrity: sha512-ZeV/Op4bZsWXuw5Tg05WwRI8BlKiRFhsixPcAM+5BKYSiUZiMKIi713tfT3drBq8+T0E1arNZgYSA9QYcglWNA==} + dependencies: + '@prisma/debug': 5.7.0 dev: false /@tsconfig/node10@1.0.9: @@ -115,13 +137,13 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /prisma@5.6.0: - resolution: {integrity: sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==} + /prisma@5.7.0: + resolution: {integrity: sha512-0rcfXO2ErmGAtxnuTNHQT9ztL0zZheQjOI/VNJzdq87C3TlGPQtMqtM+KCwU6XtmkoEr7vbCQqA7HF9IY0ST+Q==} engines: {node: '>=16.13'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 5.6.0 + '@prisma/engines': 5.7.0 dev: false /ts-node@10.9.1(@types/node@20.10.3)(typescript@5.3.2): From 5d4239e5e5605941ef7f75a7079b578ab0d5995f Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Sun, 17 Dec 2023 19:04:12 +0100 Subject: [PATCH 07/19] Fix missing directory --- apps/web/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 0a413dd06..fa7df5712 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -36,6 +36,7 @@ COPY --from=build /app/packages/api/dist/api ./packages/api COPY packages/api/package.json ./packages/api COPY packages/api/translations ./packages/api/translations +COPY packages/api/emails ./packages/api/emails RUN --mount=type=cache,id=pnpm-store,target=/app/.pnpm-store \ pnpm i --production From a60da6b2e661ed6801aeabcd3b145647e8c2b12c Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Sun, 17 Dec 2023 19:10:03 +0100 Subject: [PATCH 08/19] Fix top-level await --- .vscode/settings.json | 4 +- apps/duckguessr-old | 1 + packages/api/scripts/send-pending-emails.ts | 118 ++++++++++---------- packages/duckguessr-ml | 1 + 4 files changed, 64 insertions(+), 60 deletions(-) create mode 160000 apps/duckguessr-old create mode 160000 packages/duckguessr-ml diff --git a/.vscode/settings.json b/.vscode/settings.json index ae0ee58d6..82c61a488 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "editor.codeActionsOnSave": { - "source.fixAll": true + "source.fixAll": "explicit" }, "sqltools.connections": [ { @@ -17,5 +17,5 @@ "password": "changeme" } ], - "i18n-ally.localesPaths": ["translations"], + "i18n-ally.localesPaths": ["translations"] } diff --git a/apps/duckguessr-old b/apps/duckguessr-old new file mode 160000 index 000000000..709e012a6 --- /dev/null +++ b/apps/duckguessr-old @@ -0,0 +1 @@ +Subproject commit 709e012a6c783d5326347c89a7aaaf45996c179d diff --git a/packages/api/scripts/send-pending-emails.ts b/packages/api/scripts/send-pending-emails.ts index 7721a378b..fbc7261a2 100644 --- a/packages/api/scripts/send-pending-emails.ts +++ b/packages/api/scripts/send-pending-emails.ts @@ -16,69 +16,71 @@ const medalLevels = { } as const; -const emailsSent = []; -for (const contributionTypeStr in medalLevels) { - const contributionType = contributionTypeStr as userContributionType; - const pendingEmailContributionsForType = await prismaDmClient.userContribution.findMany({ - where: { - contribution: contributionType, - isEmailSent: false - }, orderBy: { - userId: 'asc', - totalPoints: 'asc' - }}); - if (!pendingEmailContributionsForType.length) { - console.info(`No email to send for contribution ${contributionType}`); - } else { - const pendingEmailContributionsByUser = pendingEmailContributionsForType.reduce>((acc, contribution) => ({ - ...acc, [contribution.userId]: [...((acc[contribution.userId]) || []), contribution] - }), {}); +(async () => { + const emailsSent = []; + for (const contributionTypeStr in medalLevels) { + const contributionType = contributionTypeStr as userContributionType; + const pendingEmailContributionsForType = await prismaDmClient.userContribution.findMany({ + where: { + contribution: contributionType, + isEmailSent: false + }, orderBy: { + userId: 'asc', + totalPoints: 'asc' + }}); + if (!pendingEmailContributionsForType.length) { + console.info(`No email to send for contribution ${contributionType}`); + } else { + const pendingEmailContributionsByUser = pendingEmailContributionsForType.reduce>((acc, contribution) => ({ + ...acc, [contribution.userId]: [...((acc[contribution.userId]) || []), contribution] + }), {}); - for (const [userId, pendingEmailContributionsForUser] of Object.entries(pendingEmailContributionsByUser)) { - console.info(`${pendingEmailContributionsForUser.length} contributions pending for user ${userId}`); - const initialPointsCount = pendingEmailContributionsForUser[0].totalPoints - pendingEmailContributionsForUser[0].newPoints; - const finalPointsCount = pendingEmailContributionsForUser[pendingEmailContributionsForUser.length - 1].totalPoints; - const pointsEarned = finalPointsCount - initialPointsCount; + for (const [userId, pendingEmailContributionsForUser] of Object.entries(pendingEmailContributionsByUser)) { + console.info(`${pendingEmailContributionsForUser.length} contributions pending for user ${userId}`); + const initialPointsCount = pendingEmailContributionsForUser[0].totalPoints - pendingEmailContributionsForUser[0].newPoints; + const finalPointsCount = pendingEmailContributionsForUser[pendingEmailContributionsForUser.length - 1].totalPoints; + const pointsEarned = finalPointsCount - initialPointsCount; - const medalReached = Object.entries(medalLevels[contributionType]).reduce((medalReached, [medal, medalThreshold]) => - (initialPointsCount < medalThreshold && finalPointsCount >= medalThreshold) ? parseInt(medal): medalReached, null as number|null) + const medalReached = Object.entries(medalLevels[contributionType]).reduce((medalReached, [medal, medalThreshold]) => + (initialPointsCount < medalThreshold && finalPointsCount >= medalThreshold) ? parseInt(medal): medalReached, null as number|null) - await prismaDmClient.$transaction( - pendingEmailContributionsForUser.map(({ id }) => - prismaDmClient.userContribution.update({ - where: { - id, - }, - data: { - isEmailSent: true - }, - }) - ) - ); + await prismaDmClient.$transaction( + pendingEmailContributionsForUser.map(({ id }) => + prismaDmClient.userContribution.update({ + where: { + id, + }, + data: { + isEmailSent: true + }, + }) + ) + ); - const user = await prismaDmClient.user.findUniqueOrThrow({ where: {id: parseInt(userId)}}); + const user = await prismaDmClient.user.findUniqueOrThrow({ where: {id: parseInt(userId)}}); - const locale = 'fr'; - let email; - switch (contributionType) { - case 'duckhunter': - email =(await new BookstoreApproved({ user, locale, newMedalLevel: medalReached })); - break; - case 'photographe': - email = (await new EdgesPublishedWithPhotographer({ user, locale, extraEdges: pendingEmailContributionsForUser.length, extraPhotographerPoints: pointsEarned, newMedalLevel: medalReached })); - break; - case 'createur': - email = (await new EdgesPublishedWithCreator({ user, locale, extraEdges: pendingEmailContributionsForUser.length, extraCreatorPoints: pointsEarned, newMedalLevel: medalReached })); - break; + const locale = 'fr'; + let email; + switch (contributionType) { + case 'duckhunter': + email =(await new BookstoreApproved({ user, locale, newMedalLevel: medalReached })); + break; + case 'photographe': + email = (await new EdgesPublishedWithPhotographer({ user, locale, extraEdges: pendingEmailContributionsForUser.length, extraPhotographerPoints: pointsEarned, newMedalLevel: medalReached })); + break; + case 'createur': + email = (await new EdgesPublishedWithCreator({ user, locale, extraEdges: pendingEmailContributionsForUser.length, extraCreatorPoints: pointsEarned, newMedalLevel: medalReached })); + break; + } + emailsSent.push(email) + email.send(); } - emailsSent.push(email) - email.send(); } } -} -console.log(JSON.stringify({ - 'emails_sent': emailsSent.map(emailHelper => ({ - 'to': emailHelper.getTo(), - 'subject': emailHelper.getSubject() - })) -})); + console.log(JSON.stringify({ + 'emails_sent': emailsSent.map(emailHelper => ({ + 'to': emailHelper.getTo(), + 'subject': emailHelper.getSubject() + })) + })); +})(); \ No newline at end of file diff --git a/packages/duckguessr-ml b/packages/duckguessr-ml new file mode 160000 index 000000000..eabeaf30c --- /dev/null +++ b/packages/duckguessr-ml @@ -0,0 +1 @@ +Subproject commit eabeaf30c7fa4af2282cf9b511c3935fc015b182 From f93fd2bb2852f12c671d3e665da5f3367ccdbdcc Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Sun, 17 Dec 2023 19:19:57 +0100 Subject: [PATCH 09/19] Bump submodule --- apps/duckguessr-old | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/duckguessr-old b/apps/duckguessr-old index 709e012a6..dbd39e3a6 160000 --- a/apps/duckguessr-old +++ b/apps/duckguessr-old @@ -1 +1 @@ -Subproject commit 709e012a6c783d5326347c89a7aaaf45996c179d +Subproject commit dbd39e3a6e72da60bc0855a999b5887653b95d27 From d2ac7570f386880cebf8a42bcc160395e6b09a70 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Sun, 17 Dec 2023 19:27:09 +0100 Subject: [PATCH 10/19] Fix userId type --- apps/web/src/pages/admin/presentation-text/[decision].vue | 2 +- packages/api-routes/index.ts | 2 +- packages/api/routes/presentation-text/:decision.ts | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/web/src/pages/admin/presentation-text/[decision].vue b/apps/web/src/pages/admin/presentation-text/[decision].vue index ac7ef9408..07b26abff 100644 --- a/apps/web/src/pages/admin/presentation-text/[decision].vue +++ b/apps/web/src/pages/admin/presentation-text/[decision].vue @@ -17,7 +17,7 @@ let router = useRouter(); params: { decision: currentRoute.params.decision as string }, reqBody: currentRoute.query as unknown as { sentence: string; - userId: number; + userId: string; }, }), ); diff --git a/packages/api-routes/index.ts b/packages/api-routes/index.ts index dd8016828..67eee4ac0 100644 --- a/packages/api-routes/index.ts +++ b/packages/api-routes/index.ts @@ -396,7 +396,7 @@ export class GET__global_stats__user__list extends ContractWithMethodAndUrl<{ r } export class POST__presentation_text__$decision extends ContractWithMethodAndUrl<{ params: { decision: string }; - reqBody: { sentence: string; userId: number }; + reqBody: { sentence: string; userId: string }; }> { static readonly method = "post"; static readonly url = "/presentation-text/:decision"; diff --git a/packages/api/routes/presentation-text/:decision.ts b/packages/api/routes/presentation-text/:decision.ts index fc7d74448..a7b6e973b 100644 --- a/packages/api/routes/presentation-text/:decision.ts +++ b/packages/api/routes/presentation-text/:decision.ts @@ -12,10 +12,11 @@ export const post = [ async ( ...[req, res]: ExpressCall<{ params: { decision: string }; - reqBody: { sentence: string; userId: number }; + reqBody: { sentence: string; userId: string }; }> ) => { - const { sentence, userId } = req.body; + const { sentence, userId: userIdString } = req.body; + const userId = parseInt(userIdString); const { decision } = req.params; if (!["approve", "refuse"].includes(decision as string)) { res.writeHead(400); From 523fa4409a7a2b2150cbee765411bd0456876544 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Sat, 23 Dec 2023 12:32:22 +0100 Subject: [PATCH 11/19] Fix last purcahse date display --- apps/web/src/components/LastPurchases.vue | 18 ++++++++---------- apps/web/tsconfig.json | 1 + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/LastPurchases.vue b/apps/web/src/components/LastPurchases.vue index 7f4b9c10c..37bdc46ea 100644 --- a/apps/web/src/components/LastPurchases.vue +++ b/apps/web/src/components/LastPurchases.vue @@ -19,7 +19,7 @@ :visible="false" >