Added mailchimp feature + cron services
This commit is contained in:
parent
399ed9ac3c
commit
8fd59a13d8
46
Dockerfile-Cron
Normal file
46
Dockerfile-Cron
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Install dependencies only when needed
|
||||||
|
FROM node:19-alpine AS deps
|
||||||
|
|
||||||
|
WORKDIR leCoffre
|
||||||
|
|
||||||
|
RUN npm install -D prisma@4.11.0
|
||||||
|
COPY package.json ./
|
||||||
|
|
||||||
|
RUN apk update && apk add openssh-client git
|
||||||
|
|
||||||
|
COPY id_rsa /root/.ssh/id_rsa
|
||||||
|
RUN chmod 600 ~/.ssh/id_rsa
|
||||||
|
RUN eval "$(ssh-agent -s)" && ssh-add /root/.ssh/id_rsa
|
||||||
|
RUN ssh-keyscan github.com smart-chain-fr/leCoffre-resources.git >> /root/.ssh/known_hosts
|
||||||
|
|
||||||
|
RUN npm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM node:19-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR leCoffre
|
||||||
|
|
||||||
|
COPY --from=deps leCoffre/node_modules ./node_modules
|
||||||
|
COPY --from=deps leCoffre/package.json package.json
|
||||||
|
COPY tsconfig.json tsconfig.json
|
||||||
|
COPY src src
|
||||||
|
|
||||||
|
RUN npx prisma generate
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM node:19-alpine AS production
|
||||||
|
|
||||||
|
WORKDIR leCoffre
|
||||||
|
|
||||||
|
RUN adduser -D lecoffreuser --uid 10000 && chown -R lecoffreuser .
|
||||||
|
|
||||||
|
COPY --from=builder --chown=lecoffreuser leCoffre/node_modules ./node_modules
|
||||||
|
COPY --from=builder --chown=lecoffreuser leCoffre/dist dist
|
||||||
|
COPY --from=builder --chown=lecoffreuser leCoffre/package.json ./package.json
|
||||||
|
COPY --from=builder --chown=lecoffreuser leCoffre/src/common/databases ./src/common/databases
|
||||||
|
|
||||||
|
USER lecoffreuser
|
||||||
|
|
||||||
|
CMD ["npm", "run", "cron"]
|
||||||
|
EXPOSE 3001
|
61
package-lock.json
generated
61
package-lock.json
generated
@ -9,12 +9,14 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mailchimp/mailchimp_transactional": "^1.0.50",
|
||||||
"@pinata/sdk": "^2.1.0",
|
"@pinata/sdk": "^2.1.0",
|
||||||
"@prisma/client": "^4.11.0",
|
"@prisma/client": "^4.11.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"cron": "^2.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.57",
|
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.57",
|
||||||
@ -35,9 +37,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.13",
|
"@types/cors": "^2.8.13",
|
||||||
|
"@types/cron": "^2.0.1",
|
||||||
"@types/express": "^4.17.16",
|
"@types/express": "^4.17.16",
|
||||||
"@types/jest": "^29.5.0",
|
"@types/jest": "^29.5.0",
|
||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
|
"@types/mailchimp__mailchimp_transactional": "^1.0.5",
|
||||||
|
"@types/module-alias": "^2.0.1",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"@types/node-fetch": "^2.6.3",
|
"@types/node-fetch": "^2.6.3",
|
||||||
@ -1044,6 +1049,17 @@
|
|||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@mailchimp/mailchimp_transactional": {
|
||||||
|
"version": "1.0.50",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_transactional/-/mailchimp_transactional-1.0.50.tgz",
|
||||||
|
"integrity": "sha512-SaNFseFPSDQlOYM9JTyYY6wauMu6qJ8eExo+jssFyb20ZaVvxKX1eTb3Gm5aW/4aWuxn6nofU+02sCk51//wdw==",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "13.4.10",
|
"version": "13.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.10.tgz",
|
||||||
@ -1348,6 +1364,16 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/cron": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-WHa/1rtNtD2Q/H0+YTTZoty+/5rcE66iAFX2IY+JuUoOACsevYyFkSYu/2vdw+G5LrmO7Lxowrqm0av4k3qWNQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/luxon": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/express": {
|
"node_modules/@types/express": {
|
||||||
"version": "4.17.17",
|
"version": "4.17.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
|
||||||
@ -1430,12 +1456,33 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/luxon": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-XOS5nBcgEeP2PpcqJHjCWhUCAzGfXIU8ILOSLpx2FhxqMW9KdxgCGXNOEKGVBfveKtIpztHzKK5vSRVLyW/NqA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/mailchimp__mailchimp_transactional": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mailchimp__mailchimp_transactional/-/mailchimp__mailchimp_transactional-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-5LYI3dZcyVBtg+lNxhKBHrHnNeAVvlpPM0kO6FZcjrrMALK7wistwvqI8PAns2mnveC67OSN43y6wQkK6KeTNQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/module-alias": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/module-alias/-/module-alias-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-DN/CCT1HQG6HquBNJdLkvV+4v5l/oEuwOHUPLxI+Eub0NED+lk0YUfba04WGH90EINiUrNgClkNnwGmbICeWMQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/multer": {
|
"node_modules/@types/multer": {
|
||||||
"version": "1.4.7",
|
"version": "1.4.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
|
||||||
@ -1446,9 +1493,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.16.19",
|
"version": "18.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.19.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.0.tgz",
|
||||||
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA=="
|
"integrity": "sha512-GXZxEtOxYGFchyUzxvKI14iff9KZ2DI+A6a37o6EQevtg6uO9t+aUZKcaC1Te5Ng1OnLM7K9NVVj+FbecD9cJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-fetch": {
|
"node_modules/@types/node-fetch": {
|
||||||
"version": "2.6.4",
|
"version": "2.6.4",
|
||||||
@ -2287,6 +2334,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/cron": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cron/-/cron-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-1eRRlIT0UfIqauwbG9pkg3J6CX9A6My2ytJWqAXoK0T9oJnUZTzGBNPxao0zjodIbPgf8UQWjE62BMb9eVllSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"luxon": "^3.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cron-parser": {
|
"node_modules/cron-parser": {
|
||||||
"version": "4.8.1",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.8.1.tgz",
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"build-db": "npx prisma migrate dev",
|
"build-db": "npx prisma migrate dev",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node ./dist/entries/App.js",
|
"start": "node ./dist/entries/App.js",
|
||||||
|
"cron": "node ./dist/entries/Cron.js",
|
||||||
"api:start": "npm run migrate && npm run start",
|
"api:start": "npm run migrate && npm run start",
|
||||||
"dev": "nodemon -V",
|
"dev": "nodemon -V",
|
||||||
"format": "prettier --write src",
|
"format": "prettier --write src",
|
||||||
@ -41,12 +42,14 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/smart-chain-fr/leCoffre-back#readme",
|
"homepage": "https://github.com/smart-chain-fr/leCoffre-back#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mailchimp/mailchimp_transactional": "^1.0.50",
|
||||||
"@pinata/sdk": "^2.1.0",
|
"@pinata/sdk": "^2.1.0",
|
||||||
"@prisma/client": "^4.11.0",
|
"@prisma/client": "^4.11.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"cron": "^2.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.58",
|
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.58",
|
||||||
@ -67,9 +70,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.13",
|
"@types/cors": "^2.8.13",
|
||||||
|
"@types/cron": "^2.0.1",
|
||||||
"@types/express": "^4.17.16",
|
"@types/express": "^4.17.16",
|
||||||
"@types/jest": "^29.5.0",
|
"@types/jest": "^29.5.0",
|
||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
|
"@types/mailchimp__mailchimp_transactional": "^1.0.5",
|
||||||
|
"@types/module-alias": "^2.0.1",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"@types/node-fetch": "^2.6.3",
|
"@types/node-fetch": "^2.6.3",
|
||||||
@ -84,6 +90,6 @@
|
|||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"schema": "src/common/databases/schema.prisma",
|
"schema": "src/common/databases/schema.prisma",
|
||||||
"seed": "ts-node src/common/databases/seeders/seeder2.ts"
|
"seed": "ts-node src/common/databases/seeders/seeder.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,12 @@ import { validateOrReject } from "class-validator";
|
|||||||
import authHandler from "@App/middlewares/AuthHandler";
|
import authHandler from "@App/middlewares/AuthHandler";
|
||||||
import ruleHandler from "@App/middlewares/RulesHandler";
|
import ruleHandler from "@App/middlewares/RulesHandler";
|
||||||
import documentHandler from "@App/middlewares/OfficeMembershipHandlers/DocumentHandler";
|
import documentHandler from "@App/middlewares/OfficeMembershipHandlers/DocumentHandler";
|
||||||
|
import EmailBuilder from "@Common/emails/EmailBuilder";
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
@Service()
|
@Service()
|
||||||
export default class DocumentsController extends ApiController {
|
export default class DocumentsController extends ApiController {
|
||||||
constructor(private documentsService: DocumentsService) {
|
constructor(private documentsService: DocumentsService, private emailBuilder: EmailBuilder) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +65,9 @@ export default class DocumentsController extends ApiController {
|
|||||||
//call service to get prisma entity
|
//call service to get prisma entity
|
||||||
const documentEntityCreated = await this.documentsService.create(documentEntity);
|
const documentEntityCreated = await this.documentsService.create(documentEntity);
|
||||||
|
|
||||||
|
//create email for asked document
|
||||||
|
this.emailBuilder.sendDocumentEmails(documentEntityCreated);
|
||||||
|
|
||||||
//Hydrate ressource with prisma entity
|
//Hydrate ressource with prisma entity
|
||||||
const document = Document.hydrate<Document>(documentEntityCreated, {
|
const document = Document.hydrate<Document>(documentEntityCreated, {
|
||||||
strategy: "excludeAll",
|
strategy: "excludeAll",
|
||||||
@ -105,6 +109,9 @@ export default class DocumentsController extends ApiController {
|
|||||||
//call service to get prisma entity
|
//call service to get prisma entity
|
||||||
const documentEntityUpdated: Documents = await this.documentsService.update(uid, documentEntity, req.body.refused_reason);
|
const documentEntityUpdated: Documents = await this.documentsService.update(uid, documentEntity, req.body.refused_reason);
|
||||||
|
|
||||||
|
//create email for asked document
|
||||||
|
this.emailBuilder.sendDocumentEmails(documentEntityUpdated);
|
||||||
|
|
||||||
//Hydrate ressource with prisma entity
|
//Hydrate ressource with prisma entity
|
||||||
const document = Document.hydrate<Document>(documentEntityUpdated, { strategy: "excludeAll" });
|
const document = Document.hydrate<Document>(documentEntityUpdated, { strategy: "excludeAll" });
|
||||||
|
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "users" DROP CONSTRAINT "users_contact_uid_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "users" DROP CONSTRAINT "users_office_role_uid_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "users" DROP CONSTRAINT "users_office_uid_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "users" DROP CONSTRAINT "users_roles_uid_fkey";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "email" (
|
||||||
|
"uid" TEXT NOT NULL,
|
||||||
|
"templateName" VARCHAR(255) NOT NULL,
|
||||||
|
"from" VARCHAR(255),
|
||||||
|
"to" VARCHAR(255) NOT NULL,
|
||||||
|
"subject" VARCHAR(255) NOT NULL,
|
||||||
|
"templateVariables" JSON NOT NULL DEFAULT '{}',
|
||||||
|
"cc" VARCHAR(255)[],
|
||||||
|
"cci" VARCHAR(255)[],
|
||||||
|
"sentAt" TIMESTAMP(3),
|
||||||
|
"nbTrySend" INTEGER DEFAULT 0,
|
||||||
|
"lastTrySendDate" TIMESTAMP(3),
|
||||||
|
|
||||||
|
CONSTRAINT "email_pkey" PRIMARY KEY ("uid")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "email_uid_key" ON "email"("uid");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "users" ADD CONSTRAINT "users_contact_uid_fkey" FOREIGN KEY ("contact_uid") REFERENCES "contacts"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "users" ADD CONSTRAINT "users_roles_uid_fkey" FOREIGN KEY ("roles_uid") REFERENCES "roles"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "users" ADD CONSTRAINT "users_office_role_uid_fkey" FOREIGN KEY ("office_role_uid") REFERENCES "office_roles"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "users" ADD CONSTRAINT "users_office_uid_fkey" FOREIGN KEY ("office_uid") REFERENCES "offices"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -270,6 +270,22 @@ model Rules {
|
|||||||
@@map("rules")
|
@@map("rules")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Emails {
|
||||||
|
uid String @id @unique @default(uuid())
|
||||||
|
templateName String @db.VarChar(255)
|
||||||
|
from String? @db.VarChar(255)
|
||||||
|
to String @db.VarChar(255)
|
||||||
|
subject String @db.VarChar(255)
|
||||||
|
templateVariables Json @default("{}") @db.Json
|
||||||
|
cc String[] @db.VarChar(255)
|
||||||
|
cci String[] @db.VarChar(255)
|
||||||
|
sentAt DateTime?
|
||||||
|
nbTrySend Int? @default(0)
|
||||||
|
lastTrySendDate DateTime?
|
||||||
|
|
||||||
|
@@map("email")
|
||||||
|
}
|
||||||
|
|
||||||
enum ECivility {
|
enum ECivility {
|
||||||
MALE
|
MALE
|
||||||
FEMALE
|
FEMALE
|
||||||
|
50
src/common/emails/EmailBuilder.ts
Normal file
50
src/common/emails/EmailBuilder.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
import DocumentsService from "@Services/super-admin/DocumentsService/DocumentsService";
|
||||||
|
import { Documents } from "@prisma/client";
|
||||||
|
import { Document } from "le-coffre-resources/dist/SuperAdmin";
|
||||||
|
import { Service } from "typedi";
|
||||||
|
import { ETemplates } from "./Templates/EmailTemplates";
|
||||||
|
import MailchimpService from "@Services/common/MailchimpService/MailchimpService";
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class EmailBuilder {
|
||||||
|
public constructor(private mailchimpService: MailchimpService ,private documentsService: DocumentsService){}
|
||||||
|
|
||||||
|
public async sendDocumentEmails(documentEntity: Documents){
|
||||||
|
if(documentEntity.document_status !== "ASKED" && documentEntity.document_status !== "REFUSED") return;
|
||||||
|
|
||||||
|
const documentPrisma = await this.documentsService.getByUid(documentEntity.uid, { depositor: {include: {contact: true}}, folder:{include:{ office: true}} });
|
||||||
|
if(!documentPrisma) throw new Error("Document not found");
|
||||||
|
const document = Document.hydrate<Document>(documentPrisma);
|
||||||
|
const to = document.depositor!.contact!.email;
|
||||||
|
|
||||||
|
const templateVariables = {
|
||||||
|
civility: document.depositor!.contact!.civility,
|
||||||
|
last_name: document.depositor!.contact!.last_name,
|
||||||
|
office_name: document.folder!.office!.name,
|
||||||
|
link: "http://localhost:3000"
|
||||||
|
};
|
||||||
|
|
||||||
|
let templateName = ETemplates.DOCUMENT_ASKED;
|
||||||
|
let subject = "Document Asked";
|
||||||
|
if(documentEntity.document_status === "REFUSED"){
|
||||||
|
templateName = ETemplates.DOCUMENT_REFUSED;
|
||||||
|
subject = "Document Refused";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mailchimpService.create({
|
||||||
|
templateName,
|
||||||
|
to,
|
||||||
|
subject,
|
||||||
|
templateVariables,
|
||||||
|
uid: "",
|
||||||
|
from: null,
|
||||||
|
cc: [],
|
||||||
|
cci: [],
|
||||||
|
sentAt: null,
|
||||||
|
nbTrySend: null,
|
||||||
|
lastTrySendDate: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
4
src/common/emails/Templates/EmailTemplates.ts
Normal file
4
src/common/emails/Templates/EmailTemplates.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const ETemplates = {
|
||||||
|
DOCUMENT_ASKED: "DOCUMENT_ASKED",
|
||||||
|
DOCUMENT_REFUSED: "DOCUMENT_REFUSED",
|
||||||
|
};
|
75
src/common/repositories/EmailRepository.ts
Normal file
75
src/common/repositories/EmailRepository.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import Database from "@Common/databases/database";
|
||||||
|
import BaseRepository from "@Repositories/BaseRepository";
|
||||||
|
import { Service } from "typedi";
|
||||||
|
import { Emails, Prisma } from "prisma/prisma-client";
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class EmailRepository extends BaseRepository {
|
||||||
|
constructor(private database: Database) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
protected get model() {
|
||||||
|
return this.database.getClient().emails;
|
||||||
|
}
|
||||||
|
protected get instanceDb() {
|
||||||
|
return this.database.getClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Find many emails
|
||||||
|
*/
|
||||||
|
public async findMany(query: Prisma.EmailsFindManyArgs) {
|
||||||
|
query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
||||||
|
return this.model.findMany(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Create an email
|
||||||
|
*/
|
||||||
|
public async create(email: Emails): Promise<Emails> {
|
||||||
|
const createArgs: Prisma.EmailsCreateArgs = {
|
||||||
|
data: {
|
||||||
|
templateName: email.templateName,
|
||||||
|
from: email.from,
|
||||||
|
to: email.to,
|
||||||
|
subject: email.subject,
|
||||||
|
templateVariables: email.templateVariables!,
|
||||||
|
cc: email.cc,
|
||||||
|
cci: email.cci,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return this.model.create(createArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : update given email
|
||||||
|
*/
|
||||||
|
public async update(uid: string, email: Emails): Promise<Emails> {
|
||||||
|
const updateArgs: Prisma.EmailsUpdateArgs = {
|
||||||
|
where: {
|
||||||
|
uid: uid,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
sentAt: email.sentAt,
|
||||||
|
nbTrySend: email.nbTrySend,
|
||||||
|
lastTrySendDate: email.lastTrySendDate,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.model.update(updateArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : find unique email
|
||||||
|
*/
|
||||||
|
public async findOneByUid(uid: string) {
|
||||||
|
return this.model.findUnique({
|
||||||
|
where: {
|
||||||
|
uid: uid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
14
src/entries/Cron.ts
Normal file
14
src/entries/Cron.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import "module-alias/register";
|
||||||
|
import "reflect-metadata";
|
||||||
|
import { Container } from "typedi";
|
||||||
|
import CronService from "@Services/common/CronService/CronService";
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
if(process.env["MAILCHIMP_API_KEY"] === "ppd"){
|
||||||
|
Container.get(CronService).sendMails();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
})();
|
24
src/services/common/CronService/CronService.ts
Normal file
24
src/services/common/CronService/CronService.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Service } from "typedi";
|
||||||
|
import { CronJob } from "cron";
|
||||||
|
import MailchimpService from "../MailchimpService/MailchimpService";
|
||||||
|
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class CronService {
|
||||||
|
constructor(private mailchimpService: MailchimpService) {}
|
||||||
|
|
||||||
|
public async sendMails() {
|
||||||
|
const cronJob = new CronJob("*/15 * * * * *", async () => {
|
||||||
|
try {
|
||||||
|
await this.mailchimpService.sendEmails();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start job
|
||||||
|
if (!cronJob.running) {
|
||||||
|
cronJob.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
src/services/common/MailchimpService/MailchimpService.ts
Normal file
116
src/services/common/MailchimpService/MailchimpService.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import EmailRepository from "@Repositories/EmailRepository";
|
||||||
|
import BaseService from "@Services/BaseService";
|
||||||
|
import { Emails } from "@prisma/client";
|
||||||
|
import { Service } from "typedi";
|
||||||
|
import MailchimpClient from "@mailchimp/mailchimp_transactional";
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class MailchimpService extends BaseService {
|
||||||
|
// private static readonly mailchimp = new Mailchimp(process.env.MAILCHIMP_API_KEY);
|
||||||
|
private static readonly from = "vincent.alamelle@smart-chain.fr";
|
||||||
|
|
||||||
|
constructor(private emailRepository: EmailRepository) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Get all emails
|
||||||
|
* @throws {Error} If emails cannot be get
|
||||||
|
*/
|
||||||
|
public async get(query: any): Promise<Emails[]> {
|
||||||
|
return this.emailRepository.findMany(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Create a new email
|
||||||
|
* @throws {Error} If email cannot be created
|
||||||
|
*/
|
||||||
|
public async create(emailEntity: Emails): Promise<Emails> {
|
||||||
|
emailEntity.from = MailchimpService.from;
|
||||||
|
return this.emailRepository.create(emailEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Modify an email
|
||||||
|
* @throws {Error} If email cannot be modified
|
||||||
|
*/
|
||||||
|
public async update(uid: string, emailEntity: Emails): Promise<Emails> {
|
||||||
|
return this.emailRepository.update(uid, emailEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Get a email by uid
|
||||||
|
* @throws {Error} If email cannot be get
|
||||||
|
*/
|
||||||
|
public async getByUid(uid: string, query?: any): Promise<Emails | null> {
|
||||||
|
return this.emailRepository.findOneByUid(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Function called by cron to send emails
|
||||||
|
* @throws {Error} If email cannot be sent
|
||||||
|
*/
|
||||||
|
public async sendEmails() {
|
||||||
|
const emailsToSend = await this.get({ where: { sentAt: null } });
|
||||||
|
const currentDate = new Date();
|
||||||
|
let nextTrySendDate = null;
|
||||||
|
|
||||||
|
for (const email of emailsToSend) {
|
||||||
|
//If tries exceed 10, we stop trying to send the email
|
||||||
|
if (email.nbTrySend && email.nbTrySend > 9) continue;
|
||||||
|
|
||||||
|
//If email has never been sent, we send it
|
||||||
|
if (email.nbTrySend == 0) {
|
||||||
|
nextTrySendDate = currentDate;
|
||||||
|
}
|
||||||
|
//If email has already been sent, we send it again every nbTrySend^2 minutes
|
||||||
|
else {
|
||||||
|
nextTrySendDate = new Date(email.lastTrySendDate!);
|
||||||
|
nextTrySendDate.setMinutes(nextTrySendDate.getMinutes() + Math.pow(email.nbTrySend!, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the next try send date is passed, we send the email
|
||||||
|
if (currentDate >= nextTrySendDate) {
|
||||||
|
try {
|
||||||
|
await this.sendEmail(email);
|
||||||
|
email.sentAt = currentDate;
|
||||||
|
} catch (error) {
|
||||||
|
email.lastTrySendDate = currentDate;
|
||||||
|
email.nbTrySend = email.nbTrySend! + 1;
|
||||||
|
}
|
||||||
|
await this.update(email.uid, email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendEmail(email: Emails) {
|
||||||
|
const apiKey = process.env["MAILCHIMP_API_KEY"];
|
||||||
|
const mailchimpApiClient = MailchimpClient(apiKey!);
|
||||||
|
|
||||||
|
await mailchimpApiClient.messages.sendTemplate({
|
||||||
|
template_name: email.templateName,
|
||||||
|
template_content: [],
|
||||||
|
message: {
|
||||||
|
global_merge_vars: this.buildVariables(email.templateVariables),
|
||||||
|
from_email: email.from!,
|
||||||
|
from_name: "LeCoffre.io",
|
||||||
|
subject: email.subject,
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
email: email.to,
|
||||||
|
type: "to",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildVariables(templateVariables: any) {
|
||||||
|
return Object.keys(templateVariables).map((key) => {
|
||||||
|
return {
|
||||||
|
name: key,
|
||||||
|
content: templateVariables[key],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -90,11 +90,11 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"isolatedModules": true
|
"isolatedModules": true,
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx", "src/services/common/TestService",
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user