From 1b853be84eb6664424b8fe47bfcc83ac8d57a3f8 Mon Sep 17 00:00:00 2001 From: Venceslas Date: Fri, 12 Jan 2024 19:57:32 +0000 Subject: [PATCH 01/10] Fixed analytics page bad behaviour --- pages/dashboard/analytics.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/dashboard/analytics.tsx b/pages/dashboard/analytics.tsx index 5a28bcb..fec8b32 100644 --- a/pages/dashboard/analytics.tsx +++ b/pages/dashboard/analytics.tsx @@ -45,7 +45,7 @@ const Analytics: BlitzPage = () => { Analytics.suppressFirstRenderFlicker = true; Analytics.authenticate = { redirectTo: Routes.Login() }; -Analytics.redirectAuthenticatedTo = redirectAuthenticatedTo(Routes.Planning()); +Analytics.redirectAuthenticatedTo = redirectAuthenticatedTo(Routes.Analytics()); Analytics.getLayout = (page) => getDashboardNav(page, 'Statistiques'); export default Analytics; From 317e6d00046dea29e8b0922d3ce96209f1e2e0a9 Mon Sep 17 00:00:00 2001 From: Venceslas Date: Fri, 12 Jan 2024 19:58:39 +0000 Subject: [PATCH 02/10] Allow custom mail provider --- .env.template | 2 ++ .github/workflows/release.yml | 2 ++ mail/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 8a149e3..9b63796 100644 --- a/.env.template +++ b/.env.template @@ -13,6 +13,8 @@ LYF_FROM_APPLICATION_API_URL=https://sandbox-webpos.lyf.eu/fr/plugin/Payment.asp SESSION_SECRET_KEY=generate_on_https://randomkeygen.com +SMTP_EMAIL=bde.isima.webmaster@gmail.com +SMTP_USER=bde.isima.webmaster SMTP_HOST=smtp.gmail.com SMTP_PASSWORD=your_password SMTP_PORT=587 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f0ac103..7256f21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,6 +51,8 @@ jobs: envkey_LYF_CREDIT_CARD_API_URL: ${{ secrets.LYF_CREDIT_CARD_API_URL }} envkey_NEXT_PUBLIC_FRONTEND_URL: ${{ secrets.NEXT_PUBLIC_FRONTEND_URL }} envkey_NEXT_PUBLIC_GA_TRACKING_ID: ${{ secrets.NEXT_PUBLIC_GA_TRACKING_ID }} + envkey_SMTP_EMAIL: bde.isima.webmaster@gmail.com + envkey_SMTP_USER: bde.isima.webmaster envkey_SMTP_HOST: ${{ secrets.SMTP_HOST }} envkey_SMTP_PORT: ${{ secrets.SMTP_PORT }} envkey_SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} diff --git a/mail/index.ts b/mail/index.ts index e9287fd..7bee82f 100644 --- a/mail/index.ts +++ b/mail/index.ts @@ -16,7 +16,7 @@ export const mail = { host: process.env.SMTP_HOST, port: Number(process.env.SMTP_PORT), auth: { - user: 'bde.isima.webmaster@gmail.com', + user: process.env.SMTP_USER, pass: process.env.SMTP_PASSWORD } }; @@ -26,7 +26,7 @@ export const mail = { try { return mailTransport.sendMail({ to, - from: 'bde.isima.webmaster@gmail.com', + from: process.env.SMTP_EMAIL, subject, html: compileView({ subject, From f0a66390d94c59b5115b54e7d57391d10dc4b1b0 Mon Sep 17 00:00:00 2001 From: Venceslas Date: Fri, 12 Jan 2024 22:57:06 +0000 Subject: [PATCH 03/10] hiding troll and listeux technical clubs from non-priviledged users --- app/entities/clubs/queries/getClubs.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/entities/clubs/queries/getClubs.ts b/app/entities/clubs/queries/getClubs.ts index 3d31012..ee201e8 100644 --- a/app/entities/clubs/queries/getClubs.ts +++ b/app/entities/clubs/queries/getClubs.ts @@ -4,7 +4,25 @@ import { resolver } from '@blitzjs/rpc'; type GetClubsInput = Pick; -export default resolver.pipe(async ({ where, orderBy, skip = 0, take }: GetClubsInput) => { +export default resolver.pipe(async ({ where, orderBy, skip = 0, take }: GetClubsInput, ctx) => { + console.log(ctx.session.roles); + + if (ctx.session.roles?.includes('*') != true && ctx.session.role?.includes('bde') != true) { + const limit: Prisma.ClubWhereInput = { + NOT: { + OR: [{ name: 'listeux' }, { name: 'troll' }] + } + }; + + if (where) { + where = { + AND: [where, limit] + }; + } else { + where = limit; + } + } + const clubs = await db.club.findMany({ where, orderBy, From ad377af16ac436ca353d96492b3246899d89a2d1 Mon Sep 17 00:00:00 2001 From: Venceslas Date: Fri, 12 Jan 2024 22:58:52 +0000 Subject: [PATCH 04/10] Adding vscode directory ignore rule --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c87c531..0d380cd 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,5 @@ lib-cov .vscode-test .idea +.vscode .vercel From 5a6c1cde2ce2a9892842cfd9d2b88f0b4280b19d Mon Sep 17 00:00:00 2001 From: Thomas GALPIN <45120387+Topin2001@users.noreply.github.com> Date: Thu, 23 May 2024 11:34:25 +0200 Subject: [PATCH 05/10] Enable status display (#54) * update browserslist * Added a display for the adherent status of users --- .../dashboard/cashing/CashingDialog.tsx | 5 +++++ .../hub/transactions/display/Adherent.tsx | 20 +++++++++++++++++++ package.json | 1 + yarn.lock | 6 +++--- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 app/components/hub/transactions/display/Adherent.tsx diff --git a/app/components/dashboard/cashing/CashingDialog.tsx b/app/components/dashboard/cashing/CashingDialog.tsx index 5dbc558..21886a6 100644 --- a/app/components/dashboard/cashing/CashingDialog.tsx +++ b/app/components/dashboard/cashing/CashingDialog.tsx @@ -30,6 +30,7 @@ import { isTroll } from 'app/core/utils/listeux_or_troll'; import getTransactions from 'app/entities/transactions/queries/getTransactions'; import getUser from 'app/entities/users/queries/getUser'; import getUsers from 'app/entities/users/queries/getUsers'; +import Adherent from 'app/components/hub/transactions/display/Adherent'; const Catalog = lazy(() => import('./catalog/Catalog')); const AdminTransfer = lazy(() => import('./adminTransfer/AdminTransfer')); @@ -99,6 +100,10 @@ export default function CashingDialog({ user, onSelection, onClear }) { + }> + + + }> diff --git a/app/components/hub/transactions/display/Adherent.tsx b/app/components/hub/transactions/display/Adherent.tsx new file mode 100644 index 0000000..3045152 --- /dev/null +++ b/app/components/hub/transactions/display/Adherent.tsx @@ -0,0 +1,20 @@ +import Typography, { TypographyTypeMap } from '@mui/material/Typography'; + +import { useQuery } from '@blitzjs/rpc'; + +type BalanceProps = { + getQuery: any; + queryArgs?: any; + variant?: TypographyTypeMap['props']['variant']; +}; + +export default function Adherent({ getQuery, queryArgs = {}, variant = 'h5' }: BalanceProps) { + const [user] = useQuery(getQuery, queryArgs); + const adherent = (user as any).is_member; + + return ( + + {adherent ? 'Cotisant' : 'Non-Cotisant'} + + ); +} diff --git a/package.json b/package.json index 287a19e..c2fe3cc 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@mui/x-date-pickers": "^5.0.13", "@prisma/client": "^4.8.1", "blitz": "2.0.0-beta.21", + "caniuse-lite": "^1.0.30001553", "chart.js": "3.9.1", "cuid": "^2.1.8", "date-fns": "^2.29.3", diff --git a/yarn.lock b/yarn.lock index aa68810..147acf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3013,9 +3013,9 @@ camelcase@^7.0.0: integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001426: - version "1.0.30001442" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz" - integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow== + version "1.0.30001553" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001553.tgz" + integrity sha512-N0ttd6TrFfuqKNi+pMgWJTb9qrdJu4JSpgPFLe/lrD19ugC6fZgF0pUewRowDwzdDnb9V41mFcdlYgl/PyKf4A== chalk@^2.0.0: version "2.4.2" From 38262041b7f9191d9802601785b71fb419ff23ff Mon Sep 17 00:00:00 2001 From: citorva <16229435+citorva@users.noreply.github.com> Date: Sat, 26 Oct 2024 23:43:18 +0200 Subject: [PATCH 06/10] Add ability to hide some sponsor or some clubs (#56) * Add ability to disable clubs * Add ability to disable sponsors * Automatically hides clubs used for technical purposes --- app/components/dashboard/clubs/ClubForm.tsx | 7 ++- .../dashboard/partners/PartnerForm.tsx | 7 ++- app/components/forms/validations.ts | 6 +- app/components/public/Clubs.tsx | 4 +- app/components/public/Partners.tsx | 4 +- app/entities/clubs/queries/getClubs.ts | 55 +++++++------------ app/entities/clubs/queries/getPublicClubs.ts | 23 ++++++++ app/entities/partners/queries/getPartners.ts | 37 +++++++------ .../partners/queries/getPublicPartners.ts | 20 +++++++ db/schema.prisma | 4 ++ db/seeds/users.ts | 2 +- pages/dashboard/clubs.tsx | 6 ++ pages/dashboard/partners.tsx | 6 ++ 13 files changed, 118 insertions(+), 63 deletions(-) create mode 100644 app/entities/clubs/queries/getPublicClubs.ts create mode 100644 app/entities/partners/queries/getPublicPartners.ts diff --git a/app/components/dashboard/clubs/ClubForm.tsx b/app/components/dashboard/clubs/ClubForm.tsx index c71cd50..3ba0a05 100644 --- a/app/components/dashboard/clubs/ClubForm.tsx +++ b/app/components/dashboard/clubs/ClubForm.tsx @@ -2,7 +2,7 @@ import Divider from '@mui/material/Divider'; import IconButton from '@mui/material/IconButton'; import InputAdornment from '@mui/material/InputAdornment'; import { Club } from 'db'; -import { TextField } from 'mui-rff'; +import { Switches, TextField } from 'mui-rff'; import OpenInNew from '@mui/icons-material/OpenInNewTwoTone'; @@ -43,7 +43,8 @@ export default function ClubForm(props: ClubFormProps) { facebookURL: props.initialValues?.facebookURL, twitterURL: props.initialValues?.twitterURL, instagramURL: props.initialValues?.instagramURL, - customURL: props.initialValues?.customURL + customURL: props.initialValues?.customURL, + isPublic: props.initialValues?.isPublic }} onSubmit={onSubmit} autoComplete="off" @@ -84,6 +85,8 @@ export default function ClubForm(props: ClubFormProps) { + + diff --git a/app/components/dashboard/partners/PartnerForm.tsx b/app/components/dashboard/partners/PartnerForm.tsx index cbb6a18..47eac91 100644 --- a/app/components/dashboard/partners/PartnerForm.tsx +++ b/app/components/dashboard/partners/PartnerForm.tsx @@ -1,7 +1,7 @@ import IconButton from '@mui/material/IconButton'; import InputAdornment from '@mui/material/InputAdornment'; import { Partner } from 'db'; -import { TextField } from 'mui-rff'; +import { Switches, TextField } from 'mui-rff'; import OpenInNew from '@mui/icons-material/OpenInNewTwoTone'; @@ -37,7 +37,8 @@ export default function PartnerForm(props: PartnerFormProps) { id: props.initialValues?.id, image: props.initialValues?.image, name: props.initialValues?.name, - description: props.initialValues?.description + description: props.initialValues?.description, + isPublic: props.initialValues?.isPublic }} onSubmit={onSubmit} autoComplete="off" @@ -76,6 +77,8 @@ export default function PartnerForm(props: PartnerFormProps) { + + ); } diff --git a/app/components/forms/validations.ts b/app/components/forms/validations.ts index fd8e6f2..877bccc 100644 --- a/app/components/forms/validations.ts +++ b/app/components/forms/validations.ts @@ -77,7 +77,8 @@ export const ClubInput = z facebookURL: z.string().url().optional().nullable(), twitterURL: z.string().url().optional().nullable(), instagramURL: z.string().url().optional().nullable(), - customURL: z.string().url().optional().nullable() + customURL: z.string().url().optional().nullable(), + isPublic: z.boolean().optional().nullable() }) .partial(); export type ClubInputType = z.infer; @@ -113,7 +114,8 @@ export const PartnerInput = z .optional() .nullable(), name: z.string().max(255), - description: z.string().max(3000).optional().nullable() + description: z.string().max(3000).optional().nullable(), + isPublic: z.boolean().optional().nullable() }) .partial(); export type PartnerInputType = z.infer; diff --git a/app/components/public/Clubs.tsx b/app/components/public/Clubs.tsx index 7ebe9c8..0ca0229 100644 --- a/app/components/public/Clubs.tsx +++ b/app/components/public/Clubs.tsx @@ -9,7 +9,7 @@ import { Club } from 'db'; import Image from 'next/image'; -import getClubs from 'app/entities/clubs/queries/getClubs'; +import getPublicClubs from 'app/entities/clubs/queries/getPublicClubs'; import Carousel from './carousel'; @@ -46,7 +46,7 @@ export default function Clubs() { }> - getQuery={getClubs} queryKey="clubs" /> + getQuery={getPublicClubs} queryKey="clubs" /> ); diff --git a/app/components/public/Partners.tsx b/app/components/public/Partners.tsx index 6f46568..39ad48d 100644 --- a/app/components/public/Partners.tsx +++ b/app/components/public/Partners.tsx @@ -10,7 +10,7 @@ import { Partner } from 'db'; import Image from 'next/image'; import Link from 'app/core/lib/Link'; -import getPartners from 'app/entities/partners/queries/getPartners'; +import getPublicPartners from 'app/entities/partners/queries/getPublicPartners'; import Carousel from './carousel'; @@ -52,7 +52,7 @@ export default function Partners() { }> - getQuery={getPartners} queryKey="partners" /> + getQuery={getPublicPartners} queryKey="partners" /> ); diff --git a/app/entities/clubs/queries/getClubs.ts b/app/entities/clubs/queries/getClubs.ts index ee201e8..287e8a7 100644 --- a/app/entities/clubs/queries/getClubs.ts +++ b/app/entities/clubs/queries/getClubs.ts @@ -4,40 +4,25 @@ import { resolver } from '@blitzjs/rpc'; type GetClubsInput = Pick; -export default resolver.pipe(async ({ where, orderBy, skip = 0, take }: GetClubsInput, ctx) => { - console.log(ctx.session.roles); - - if (ctx.session.roles?.includes('*') != true && ctx.session.role?.includes('bde') != true) { - const limit: Prisma.ClubWhereInput = { - NOT: { - OR: [{ name: 'listeux' }, { name: 'troll' }] - } +export default resolver.pipe( + resolver.authorize(['*', 'bde']), + async ({ where, orderBy, skip = 0, take }: GetClubsInput, _ctx) => { + const clubs = await db.club.findMany({ + where, + orderBy, + take, + skip + }); + + const count = await db.club.count({ where }); + const hasMore = typeof take === 'number' ? skip + take < count : false; + const nextPage = hasMore ? { take, skip: skip + take! } : null; + + return { + clubs, + nextPage, + hasMore, + count }; - - if (where) { - where = { - AND: [where, limit] - }; - } else { - where = limit; - } } - - const clubs = await db.club.findMany({ - where, - orderBy, - take, - skip - }); - - const count = await db.club.count({ where }); - const hasMore = typeof take === 'number' ? skip + take < count : false; - const nextPage = hasMore ? { take, skip: skip + take! } : null; - - return { - clubs, - nextPage, - hasMore, - count - }; -}); +); diff --git a/app/entities/clubs/queries/getPublicClubs.ts b/app/entities/clubs/queries/getPublicClubs.ts new file mode 100644 index 0000000..ebb24ed --- /dev/null +++ b/app/entities/clubs/queries/getPublicClubs.ts @@ -0,0 +1,23 @@ +import db from 'db'; + +import { resolver } from '@blitzjs/rpc'; + +type GetPublicClubsInput = {}; + +export default resolver.pipe(async ({}: GetPublicClubsInput, _ctx) => { + const clubs = await db.club.findMany({ + where: { + name: { + notIn: ['*', 'listeux'] + }, + isPublic: true + }, + orderBy: { + name: 'asc' + } + }); + + return { + clubs + }; +}); diff --git a/app/entities/partners/queries/getPartners.ts b/app/entities/partners/queries/getPartners.ts index d1010d8..1923d2f 100644 --- a/app/entities/partners/queries/getPartners.ts +++ b/app/entities/partners/queries/getPartners.ts @@ -4,22 +4,25 @@ import { resolver } from '@blitzjs/rpc'; type GetPartnersInput = Pick; -export default resolver.pipe(async ({ where, orderBy, skip = 0, take }: GetPartnersInput) => { - const partners = await db.partner.findMany({ - where, - orderBy, - take, - skip - }); +export default resolver.pipe( + resolver.authorize(['*', 'bde']), + async ({ where, orderBy, skip = 0, take }: GetPartnersInput, _ctx) => { + const partners = await db.partner.findMany({ + where, + orderBy, + take, + skip + }); - const count = await db.partner.count({ where }); - const hasMore = typeof take === 'number' ? skip + take < count : false; - const nextPage = hasMore ? { take, skip: skip + take! } : null; + const count = await db.partner.count({ where }); + const hasMore = typeof take === 'number' ? skip + take < count : false; + const nextPage = hasMore ? { take, skip: skip + take! } : null; - return { - partners, - nextPage, - hasMore, - count - }; -}); + return { + partners, + nextPage, + hasMore, + count + }; + } +); diff --git a/app/entities/partners/queries/getPublicPartners.ts b/app/entities/partners/queries/getPublicPartners.ts new file mode 100644 index 0000000..2382f80 --- /dev/null +++ b/app/entities/partners/queries/getPublicPartners.ts @@ -0,0 +1,20 @@ +import db, { Prisma } from 'db'; + +import { resolver } from '@blitzjs/rpc'; + +type GetPublicPartnersInput = {}; + +export default resolver.pipe(async ({}: GetPublicPartnersInput) => { + const partners = await db.partner.findMany({ + where: { + isPublic: true + }, + orderBy: { + name: 'asc' + } + }); + + return { + partners + }; +}); diff --git a/db/schema.prisma b/db/schema.prisma index 99e64b5..09daf70 100644 --- a/db/schema.prisma +++ b/db/schema.prisma @@ -115,6 +115,8 @@ model Club { instagramURL String? customURL String? + isPublic Boolean @default(true) + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt Event Event[] @relation("EventClub") @@ -127,6 +129,8 @@ model Partner { description String? image String? + isPublic Boolean @default(true) + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } diff --git a/db/seeds/users.ts b/db/seeds/users.ts index 824ddab..a107bb6 100644 --- a/db/seeds/users.ts +++ b/db/seeds/users.ts @@ -26,7 +26,7 @@ const users = async (db) => { firstname: 'Venceslas', nickname: 'venny', image: 'https://i.imgur.com/VbdBkxz.png', - email: 'duet.venceslas@orange.fr', + email: 'contact@citorva.fr', card: 1463, balance: 0, roles: '*', diff --git a/pages/dashboard/clubs.tsx b/pages/dashboard/clubs.tsx index 0bfeb70..810b2f2 100644 --- a/pages/dashboard/clubs.tsx +++ b/pages/dashboard/clubs.tsx @@ -1,3 +1,4 @@ +import { Checkbox } from '@mui/material'; import Avatar from '@mui/material/Avatar'; import GroupsIcon from '@mui/icons-material/Groups'; @@ -56,6 +57,11 @@ const columns = [ id: 'email', headerName: 'Email', searchCriteria: 'contains' + }, + { + id: 'isPublic', + headerName: 'Public', + render: (row) => } ]; diff --git a/pages/dashboard/partners.tsx b/pages/dashboard/partners.tsx index 0f2fee5..5d345a6 100644 --- a/pages/dashboard/partners.tsx +++ b/pages/dashboard/partners.tsx @@ -1,3 +1,4 @@ +import { Checkbox } from '@mui/material'; import Avatar from '@mui/material/Avatar'; import AccountBalance from '@mui/icons-material/AccountBalance'; @@ -55,6 +56,11 @@ const columns = [ id: 'description', headerName: 'Description', searchCriteria: 'contains' + }, + { + id: 'isPublic', + headerName: 'Public', + render: (row) => } ]; From db96f5e5f654796704376aa0b7159c9f430f6ca8 Mon Sep 17 00:00:00 2001 From: citorva Date: Sun, 27 Oct 2024 09:40:01 +0100 Subject: [PATCH 07/10] Updating lockfile --- yarn.lock | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yarn.lock b/yarn.lock index 147acf8..7c65fd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3017,6 +3017,11 @@ caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.300014 resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001553.tgz" integrity sha512-N0ttd6TrFfuqKNi+pMgWJTb9qrdJu4JSpgPFLe/lrD19ugC6fZgF0pUewRowDwzdDnb9V41mFcdlYgl/PyKf4A== +caniuse-lite@^1.0.30001553: + version "1.0.30001672" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001672.tgz#02ac296ad4765c6c4f93031525f60cf8bdf4a44f" + integrity sha512-XhW1vRo1ob6aeK2w3rTohwTPBLse/rvjq+s3RTSBwnlZqoFFjx9cHsShJjAIbLsLjyoacaTxpLZy9v3gg6zypw== + chalk@^2.0.0: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" From 60cad8ca4a98b975a23aa0ab807a5a8aac807fb7 Mon Sep 17 00:00:00 2001 From: citorva Date: Mon, 28 Oct 2024 11:24:47 +0000 Subject: [PATCH 08/10] Update devcontainer --- .devcontainer/devcontainer.json | 53 ++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index aa01605..a2bcac4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,36 +2,41 @@ // https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/javascript-node-postgres // Update the VARIANT arg in docker-compose.yml to pick a Node.js version { + "$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.schema.json", "name": "bde-isima", "dockerComposeFile": "docker-compose.yml", "service": "bde_isima", "workspaceFolder": "/workspace", // Set *default* container specific settings.json values on container create. - "settings": { - "typescript.preferences.importModuleSpecifier": "non-relative", - "typescript.tsdk": "node_modules/typescript/lib", - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.formatOnPaste": false, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, - "files.trimTrailingWhitespace": true + "customizations": { + "vscode": { + "settings": { + "typescript.preferences.importModuleSpecifier": "non-relative", + "typescript.tsdk": "node_modules/typescript/lib", + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.formatOnPaste": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "files.trimTrailingWhitespace": true + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-azuretools.vscode-docker", + "mikestead.dotenv", + "editorconfig.editorconfig", + "dbaeumer.vscode-eslint", + "graphql.vscode-graphql", + "ms-vscode.vscode-typescript-next", + "esbenp.prettier-vscode", + "mgmcdermott.vscode-language-babel", + "prisma.prisma", + "shardulm94.trailing-spaces", + "bradlc.vscode-tailwindcss" + ] + } }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "ms-azuretools.vscode-docker", - "mikestead.dotenv", - "editorconfig.editorconfig", - "dbaeumer.vscode-eslint", - "graphql.vscode-graphql", - "ms-vscode.vscode-typescript-next", - "esbenp.prettier-vscode", - "mgmcdermott.vscode-language-babel", - "prisma.prisma", - "shardulm94.trailing-spaces", - "bradlc.vscode-tailwindcss" - ], // Use 'forwardPorts' to make a list of ports inside the container available locally. "forwardPorts": [ 3000 From 9337b5cb560bc186c53908b97d5b80d5e94f9f7a Mon Sep 17 00:00:00 2001 From: citorva <16229435+citorva@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:48:46 +0100 Subject: [PATCH 09/10] Remove topup-related code (#58) * Remove topup-related code * decrease log level to warn * Manage discord server url through github secrets --------- Co-authored-by: Topin2001 --- .env.template | 2 +- .github/workflows/release.yml | 1 + app/blitz-server.ts | 5 + .../transactions/display/TransactionsCard.tsx | 16 +-- .../transactions/operations/topUp/TopUp.tsx | 41 ------ .../operations/topUp/TopUpDialog.tsx | 43 ------ .../operations/topUp/TopUpForm.tsx | 60 -------- app/core/utils/topup.ts | 47 ------- .../transactions/mutations/requestTopUp.ts | 131 ------------------ pages/api/topup/[token].ts | 107 -------------- pages/hub/index.tsx | 10 +- 11 files changed, 14 insertions(+), 449 deletions(-) delete mode 100644 app/components/hub/transactions/operations/topUp/TopUp.tsx delete mode 100644 app/components/hub/transactions/operations/topUp/TopUpDialog.tsx delete mode 100644 app/components/hub/transactions/operations/topUp/TopUpForm.tsx delete mode 100644 app/core/utils/topup.ts delete mode 100644 app/entities/transactions/mutations/requestTopUp.ts delete mode 100644 pages/api/topup/[token].ts diff --git a/.env.template b/.env.template index 9b63796..6244bd2 100644 --- a/.env.template +++ b/.env.template @@ -4,7 +4,7 @@ DATABASE_URL=postgresql://postgres:postgres@bde_isima_pg:5432/postgres NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000 NEXT_PUBLIC_GA_TRACKING_ID=REDACTED -NEXT_PUBLIC_DISCORD_SERVER_URL=https://discord.gg/bnJ3narzF3 +NEXT_PUBLIC_DISCORD_SERVER_URL=REDACTED LYF_API_VENDOR_ID=deebb957-9025-4894-b52d-24493cdb7278 LYF_API_SECRET_KEY=B52DFC6C7F4AA054CDB08E38B2298C97A4A12039 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7256f21..9457851 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,6 +51,7 @@ jobs: envkey_LYF_CREDIT_CARD_API_URL: ${{ secrets.LYF_CREDIT_CARD_API_URL }} envkey_NEXT_PUBLIC_FRONTEND_URL: ${{ secrets.NEXT_PUBLIC_FRONTEND_URL }} envkey_NEXT_PUBLIC_GA_TRACKING_ID: ${{ secrets.NEXT_PUBLIC_GA_TRACKING_ID }} + envkey_NEXT_PUBLIC_DISCORD_SERVER_URL: ${{ secrets.NEXT_PUBLIC_DISCORD_SERVER_URL }} envkey_SMTP_EMAIL: bde.isima.webmaster@gmail.com envkey_SMTP_USER: bde.isima.webmaster envkey_SMTP_HOST: ${{ secrets.SMTP_HOST }} diff --git a/app/blitz-server.ts b/app/blitz-server.ts index ec61602..2dfcbed 100644 --- a/app/blitz-server.ts +++ b/app/blitz-server.ts @@ -2,10 +2,15 @@ import db from 'db'; import { AuthServerPlugin, PrismaStorage, simpleRolesIsAuthorized } from '@blitzjs/auth'; import { setupBlitzServer } from '@blitzjs/next'; +import { BlitzLogger } from 'blitz'; import { authConfig } from 'app/blitz-client'; export const { gSSP, gSP, api } = setupBlitzServer({ + logger: BlitzLogger({ + minLevel: 'warn', + colorizePrettyLogs: true, + }), plugins: [ AuthServerPlugin({ ...authConfig, diff --git a/app/components/hub/transactions/display/TransactionsCard.tsx b/app/components/hub/transactions/display/TransactionsCard.tsx index e1667c6..4d0b4a7 100644 --- a/app/components/hub/transactions/display/TransactionsCard.tsx +++ b/app/components/hub/transactions/display/TransactionsCard.tsx @@ -1,6 +1,5 @@ import { Suspense } from 'react'; -import Badge from '@mui/material/Badge'; import Button from '@mui/material/Button'; import ButtonGroup from '@mui/material/ButtonGroup'; import Card from '@mui/material/Card'; @@ -9,7 +8,6 @@ import Typography from '@mui/material/Typography'; import CompareArrows from '@mui/icons-material/CompareArrowsTwoTone'; import History from '@mui/icons-material/HistoryTwoTone'; -import LocalAtm from '@mui/icons-material/LocalAtmTwoTone'; import { useAuthenticatedSession } from '@blitzjs/auth'; @@ -17,7 +15,7 @@ import Balance from 'app/components/hub/transactions/display/Balance'; import RecentTransactions from 'app/components/hub/transactions/display/RecentTransactions'; import getCurrentUser from 'app/entities/users/queries/getCurrentUser'; -export default function TransactionsCard({ openTransfer, openHistory, openTopUp }) { +export default function TransactionsCard({ openTransfer, openHistory }) { const session = useAuthenticatedSession(); const FallbackComponent = [...Array(10).keys()].map((x) => ( @@ -53,18 +51,6 @@ export default function TransactionsCard({ openTransfer, openHistory, openTopUp Historique - - - - ); diff --git a/app/components/hub/transactions/operations/topUp/TopUp.tsx b/app/components/hub/transactions/operations/topUp/TopUp.tsx deleted file mode 100644 index a17a7dd..0000000 --- a/app/components/hub/transactions/operations/topUp/TopUp.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useState } from 'react'; - -import { useAuthenticatedSession } from '@blitzjs/auth'; -import { useMutation } from '@blitzjs/rpc'; - -import { TopUpInputType } from 'app/components/forms/validations'; -import Snackbar from 'app/core/layouts/Snackbar'; -import useSnackbar from 'app/entities/hooks/useSnackbar'; -import requestTopUp, { PaymentMethod } from 'app/entities/transactions/mutations/requestTopUp'; - -import TopUpForm from './TopUpForm'; - -export default function TopUp() { - const { open, message, severity, onClose, onShow } = useSnackbar(); - const [paymentMethod, setPaymentMethod] = useState('credit'); - const [topUp] = useMutation(requestTopUp); - - const beforeSubmit = (paymentMethod: PaymentMethod) => () => setPaymentMethod(paymentMethod); - - const onSuccess = (data: TopUpInputType) => { - topUp({ - amount: data.amount, - method: paymentMethod - }).then( - (url) => { - window.location.assign(url as string); - }, - (error) => { - onShow('error', error.message); - } - ); - }; - - return ( - <> - - - - - ); -} diff --git a/app/components/hub/transactions/operations/topUp/TopUpDialog.tsx b/app/components/hub/transactions/operations/topUp/TopUpDialog.tsx deleted file mode 100644 index 40b8380..0000000 --- a/app/components/hub/transactions/operations/topUp/TopUpDialog.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import IconButton from '@mui/material/IconButton'; -import NoSsr from '@mui/material/NoSsr'; - -import Close from '@mui/icons-material/CloseTwoTone'; - -import TopUp from 'app/components/hub/transactions/operations/topUp/TopUp'; -import SlideTransition from 'app/core/layouts/SlideTransition'; -import { useMediaQuery } from 'app/core/styles/theme'; - -type TopUpDialogProps = { - isOpen: boolean; - onClose: () => void; -}; - -export default function TopUpDialog({ isOpen, onClose }: TopUpDialogProps) { - const fullScreen = useMediaQuery('md'); - - return ( - - - - - - - - - - - - - - ); -} diff --git a/app/components/hub/transactions/operations/topUp/TopUpForm.tsx b/app/components/hub/transactions/operations/topUp/TopUpForm.tsx deleted file mode 100644 index f3a5a3e..0000000 --- a/app/components/hub/transactions/operations/topUp/TopUpForm.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import Button from '@mui/material/Button'; -import Typography from '@mui/material/Typography'; - -import Image from 'next/image'; - -import EnhancedTextField from 'app/components/forms/EnhancedTextfield'; -import { FORM_ERROR, Form } from 'app/components/forms/Form'; -import { TopUpInput, TopUpInputType } from 'app/components/forms/validations'; -import { PaymentMethod } from 'app/entities/transactions/mutations/requestTopUp'; - -type TopUpFormProps = { - onSuccess: (values: TopUpInputType) => void; - beforeSubmit: (paymentMethod: PaymentMethod) => () => void; -}; - -export default function TopUpForm(props: TopUpFormProps) { - const onSubmit = async (values) => { - try { - await props.onSuccess(values); - } catch (error) { - return { - [FORM_ERROR]: 'Sorry, we had an unexpected error. Please try again. - ' + error.toString() - }; - } - }; - - return ( -
- - -
- - - -
- - - Si vous rencontrez un problème lors de votre rechargement, contactez un membre BDE - - - ); -} diff --git a/app/core/utils/topup.ts b/app/core/utils/topup.ts deleted file mode 100644 index 3ed675f..0000000 --- a/app/core/utils/topup.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { BinaryLike, KeyObject, createHmac } from 'crypto'; - -export function makeMerchantReference(card: number, timestamp: number) { - const card_prefix = card > 0 ? 'p' : 'm'; - - return `r${card_prefix}${Math.abs(card)}t${Math.ceil(timestamp)}`; -} - -export function makeShopOrderReference(card: number, amount: number) { - const card_prefix = card > 0 ? 'p' : 'm'; - - return `o${card_prefix}${Math.abs(card)}a${amount}`; -} - -export function makeHmac(elements: any[], secret: BinaryLike | KeyObject) { - return createHmac('sha1', secret).update(elements.join('*')).digest('hex'); -} - -export type TopUpInfo = { - userId: string; - card: number; - amount: number; - reference: string; - orderReference: string; - creationDate: number; - byCreditCard: boolean; -}; - -export function generateTopUpToken(info: TopUpInfo, secret: BinaryLike | KeyObject) { - let ret = Buffer.from(JSON.stringify(info)).toString('base64url'); - - return `${ret}.${createHmac('sha256', secret).update(ret).digest('hex')}`; -} - -export function parseTopUpToken(token: string, secret: BinaryLike | KeyObject) { - let data = token.split('.'); - - if (data.length != 2) return null; - - if (data[1] != createHmac('sha256', secret).update(data[0]).digest('hex')) return null; - - try { - return JSON.parse(Buffer.from(data[0], 'base64url').toString('utf8')); - } catch { - return null; - } -} diff --git a/app/entities/transactions/mutations/requestTopUp.ts b/app/entities/transactions/mutations/requestTopUp.ts deleted file mode 100644 index 05f3c4d..0000000 --- a/app/entities/transactions/mutations/requestTopUp.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Ctx } from 'blitz'; - -import { resolver } from '@blitzjs/rpc'; - -import { generateTopUpToken, makeHmac, makeMerchantReference, makeShopOrderReference } from 'app/core/utils/topup'; - -type RequestTopUpInput = { - amount: number; - method: PaymentMethod; -}; - -export type PaymentMethod = 'credit' | 'lyf'; - -function generateHmac(data: URLSearchParams, additionalData: string, byCreditCard: boolean): string { - let list = [data.get('lang')]; - - let common = [ - data.get('posUuid'), - data.get('shopReference'), - data.get('shopOrderReference'), - data.get('deliveryFeesAmount'), - data.get('amount'), - data.get('currency') - ]; - - if (byCreditCard) { - list = list.concat(common); - list = list.concat([ - data.get('onSuccess'), - data.get('onError'), - additionalData, - data.get('callBackRequired'), - data.get('mode'), - data.get('address'), - data.get('city'), - data.get('country'), - data.get('zipCode') - ]); - } else { - list = list.concat([data.get('version'), data.get('timestamp')]); - list = list.concat(common); - list = list.concat([ - data.get('mode'), - data.get('onSuccess'), - data.get('onCancel'), - data.get('onError'), - additionalData, - data.get('enforcedIdentification') - ]); - } - - return makeHmac(list, `${process.env.LYF_API_SECRET_KEY}`); -} - -function prepareRequest(id: string, card: number, amount: number, byCreditCard: boolean): URLSearchParams { - const body = new URLSearchParams(); - - const timestamp = Math.floor(+new Date() / 1000); - const tAmount = Math.round(amount * 100); - - const shopReference = makeMerchantReference(card, timestamp); - const shopOrderReference = makeShopOrderReference(card, tAmount); - - const token = generateTopUpToken( - { - userId: id, - card, - amount: tAmount, - reference: shopReference, - orderReference: shopOrderReference, - creationDate: timestamp, - byCreditCard - }, - `${process.env.SESSION_SECRET_KEY}` - ); - - const userCallbackUrl = `${process.env.NEXT_PUBLIC_FRONTEND_URL}/hub`; - const additionalData = `{"callbackUrl":"${process.env.NEXT_PUBLIC_FRONTEND_URL}/api/topup/${token}"}`; - - // Common request fields - body.append('lang', 'fr'); - body.append('version', 'v2.0'); - body.append('posUuid', `${process.env.LYF_API_VENDOR_ID}`); - body.append('shopReference', shopReference); - body.append('shopOrderReference', shopOrderReference); - body.append('mode', 'IMMEDIATE'); - body.append('amount', `${tAmount}`); - body.append('deliveryFeesAmount', '0'); - body.append('currency', 'EUR'); - body.append('onSuccess', userCallbackUrl); - body.append('onError', userCallbackUrl); - body.append('additionalDataEncoded', Buffer.from(additionalData).toString('base64')); - - // Method-specific fields - if (byCreditCard) { - body.append('caseNumber', '01234'); - body.append('callBackRequired', 'true'); - body.append('country', 'FR'); - - if (process.env.NODE_ENV === 'development') { - body.append('address', '7 Some Street'); - body.append('city', 'Bigcity'); - body.append('zipCode', '01234'); - } else { - body.append('address', ''); - body.append('city', ''); - body.append('zipCode', ''); - } - } else { - body.append('onCancel', userCallbackUrl); - body.append('timestamp', `${timestamp}`); - body.append('enforcedIdentification', 'false'); - } - - body.append('mac', generateHmac(body, additionalData, byCreditCard)); - - return body; -} - -export default resolver.pipe(resolver.authorize(), async (input: RequestTopUpInput, ctx: Ctx) => { - if (Number.isNaN(input.amount) || input.amount <= 0 || input.amount >= 1000) { - throw new Error('Valeur invalide'); - } - - const byCreditCard = input.method == 'credit'; - const req = prepareRequest(ctx.session.userId as string, ctx.session.card as number, input.amount, byCreditCard); - - return `${ - byCreditCard ? process.env.LYF_CREDIT_CARD_API_URL : process.env.LYF_FROM_APPLICATION_API_URL - }?${req.toString()}`; -}); diff --git a/pages/api/topup/[token].ts b/pages/api/topup/[token].ts deleted file mode 100644 index 25fb024..0000000 --- a/pages/api/topup/[token].ts +++ /dev/null @@ -1,107 +0,0 @@ -import db from 'db'; -import { NextApiRequest, NextApiResponse } from 'next'; - -import { makeHmac, makeMerchantReference, makeShopOrderReference, parseTopUpToken } from 'app/core/utils/topup'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method === 'POST') { - const { body, query } = req; - - const token = query.token as string; - - const tokenInfo = parseTopUpToken(token, `${process.env.SESSION_SECRET_KEY}`); - - if (tokenInfo == null) { - res.status(404).send('BAD TOKEN'); - return; - } - - // Data retrieval - - const { - posUuid, - shopReference, - shopOrderReference, - amount, - discount, - currency, - status, - creationDate, - transactionUuid, - additionalData, - mac - } = body; - - // Verification of data authenticity - - const testMAC = makeHmac( - [ - posUuid, - shopReference, - shopOrderReference, - amount, - discount, - currency, - status, - creationDate, - transactionUuid, - additionalData - ], - `${process.env.LYF_API_SECRET_KEY}` - ).toUpperCase(); - - if (testMAC != mac) { - res.status(400).send('BAD MAC'); - return; - } - - // Verification of data consistency - - if ( - posUuid != `${process.env.LYF_API_VENDOR_ID}` || - shopReference != makeMerchantReference(tokenInfo.card, tokenInfo.creationDate) || - shopOrderReference != makeShopOrderReference(tokenInfo.card, tokenInfo.amount) || - amount != tokenInfo.amount || - currency != 'EUR' || - ['VALIDATED', 'REFUSED'].indexOf(status) <= -1 - ) { - res.status(400).send('INCONSISTENT DATA'); - return; - } - - // Checking the status of the request - - if (status == 'VALIDATED') { - // Adding money to the user - - const user = await db.user.findUnique({ where: { id: tokenInfo.userId } }); - - if (user != null) { - const qAmount = amount / 100; - - await Promise.all([ - db.transaction.create({ - data: { - amount: qAmount, - description: `Rechargement +${qAmount}€`, - type: 'CREDIT', - user: { connect: { id: tokenInfo.userId } }, - prevBalance: user.balance - } - }), - db.user.update({ - where: { id: tokenInfo.userId }, - data: { balance: { increment: qAmount } } - }) - ]); - res.status(200).send('OK'); - } else { - res.status(500).send('UNKNOWN USER'); - } - } else { - res.status(200).send('OK'); - } - } else { - res.status(400).send('BAD REQUEST'); - } -} diff --git a/pages/hub/index.tsx b/pages/hub/index.tsx index 32b9a06..5f11bc0 100644 --- a/pages/hub/index.tsx +++ b/pages/hub/index.tsx @@ -1,5 +1,7 @@ import { useState } from 'react'; +import { Alert } from '@mui/material'; + import { BlitzPage, Routes } from '@blitzjs/next'; import Upcoming from 'app/components/hub/events/Upcoming'; @@ -7,14 +9,12 @@ import DiscordButton from 'app/components/hub/home/DiscordButton'; import News from 'app/components/hub/home/News'; import TransactionsCard from 'app/components/hub/transactions/display/TransactionsCard'; import HistoryDialog from 'app/components/hub/transactions/operations/history/HistoryDialog'; -import TopUpDialog from 'app/components/hub/transactions/operations/topUp/TopUpDialog'; import TransferDialog from 'app/components/hub/transactions/operations/transfer/TransferDialog'; import getHubNav from 'app/components/nav/hub/getHubNav'; const Hub: BlitzPage = () => { const [isTransferOpen, setIsTransferOpen] = useState(false); const [isHistoryOpen, setIsHistoryOpen] = useState(false); - const [isTopUpOpen, setIsTopUpOpen] = useState(false); const toggleDialog = (fn, open) => () => fn(open); @@ -24,6 +24,10 @@ const Hub: BlitzPage = () => { style={{ gridTemplateColumns: '1fr 310px' }} >
+ + Suite à des problèmes techniques, le rechargement en ligne a été temporairement désactivé. Veuillez + vous rapprocher d'un membre du BDE afin de recharger votre compte +
@@ -32,12 +36,10 @@ const Hub: BlitzPage = () => { - ); From 9e6541fb3e54560f3b40faf1a9df270f3d66da8f Mon Sep 17 00:00:00 2001 From: citorva <16229435+citorva@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:43:54 +0100 Subject: [PATCH 10/10] Remove Lyf-related environment variables --- .env.template | 5 ----- .github/workflows/release.yml | 4 ---- 2 files changed, 9 deletions(-) diff --git a/.env.template b/.env.template index 6244bd2..d6e3114 100644 --- a/.env.template +++ b/.env.template @@ -6,11 +6,6 @@ NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000 NEXT_PUBLIC_GA_TRACKING_ID=REDACTED NEXT_PUBLIC_DISCORD_SERVER_URL=REDACTED -LYF_API_VENDOR_ID=deebb957-9025-4894-b52d-24493cdb7278 -LYF_API_SECRET_KEY=B52DFC6C7F4AA054CDB08E38B2298C97A4A12039 -LYF_CREDIT_CARD_API_URL=https://sandbox-webpos.lyf.eu/fr/plugin/PaymentCb.aspx -LYF_FROM_APPLICATION_API_URL=https://sandbox-webpos.lyf.eu/fr/plugin/Payment.aspx - SESSION_SECRET_KEY=generate_on_https://randomkeygen.com SMTP_EMAIL=bde.isima.webmaster@gmail.com diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9457851..28511bd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,10 +45,6 @@ jobs: envkey_CONTACT_MAIL: ${{ secrets.CONTACT_MAIL }} envkey_DATABASE_URL: ${{ secrets.DATABASE_URL }} envkey_SESSION_SECRET_KEY: ${{ secrets.SESSION_SECRET_KEY }} - envkey_LYF_API_VENDOR_ID: ${{ secrets.LYF_API_VENDOR_ID }} - envkey_LYF_API_SECRET_KEY: ${{ secrets.LYF_API_SECRET_KEY }} - envkey_LYF_FROM_APPLICATION_API_URL: ${{ secrets.LYF_FROM_APPLICATION_API_URL }} - envkey_LYF_CREDIT_CARD_API_URL: ${{ secrets.LYF_CREDIT_CARD_API_URL }} envkey_NEXT_PUBLIC_FRONTEND_URL: ${{ secrets.NEXT_PUBLIC_FRONTEND_URL }} envkey_NEXT_PUBLIC_GA_TRACKING_ID: ${{ secrets.NEXT_PUBLIC_GA_TRACKING_ID }} envkey_NEXT_PUBLIC_DISCORD_SERVER_URL: ${{ secrets.NEXT_PUBLIC_DISCORD_SERVER_URL }}