From a88e37500f69f5216d415f552365cfad4def9a75 Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Sun, 5 Sep 2021 20:26:13 +0300 Subject: [PATCH] feat: updating user email controller --- .adonisrc.json | 22 +- .env.example | 7 + ace-manifest.json | 23 +- app/Controllers/Http/Api/v1/MeController.ts | 41 ++ app/Datatypes/Enums/StatusCodeEnum.ts | 1 + app/Services/MeService.ts | 95 +++++ config/mail.ts | 76 ++++ contracts/mail.ts | 15 + env.ts | 6 + package-lock.json | 391 +++++++++++++++++- package.json | 2 + .../views/emails/change_email_confirm.edge | 4 + routes/apis/v1/me.ts | 6 + tsconfig.json | 35 +- 14 files changed, 704 insertions(+), 20 deletions(-) create mode 100644 config/mail.ts create mode 100644 contracts/mail.ts create mode 100644 resources/views/emails/change_email_confirm.edge diff --git a/.adonisrc.json b/.adonisrc.json index 809f844..f5cf765 100644 --- a/.adonisrc.json +++ b/.adonisrc.json @@ -4,7 +4,8 @@ "./commands", "@adonisjs/core/commands", "@adonisjs/repl/build/commands", - "@adonisjs/lucid/build/commands" + "@adonisjs/lucid/build/commands", + "@adonisjs/mail/build/commands" ], "exceptionHandlerNamespace": "App/Exceptions/Handler", "namespaces": { @@ -22,14 +23,27 @@ "Database": "database", "Contracts": "contracts" }, - "preloads": ["./routes/index", "./start/kernel"], + "preloads": [ + "./routes/index", + "./start/kernel" + ], "providers": [ "./providers/AppProvider", "./providers/CloudinaryProvider", "@adonisjs/core", "@adonisjs/lucid", "@adonisjs/auth", - "@adonisjs/redis" + "@adonisjs/redis", + "@adonisjs/mail", + "@adonisjs/view" ], - "aceProviders": ["@adonisjs/repl"] + "aceProviders": [ + "@adonisjs/repl" + ], + "metaFiles": [ + { + "pattern": "resources/views/**/*.edge", + "reloadServer": false + } + ] } diff --git a/.env.example b/.env.example index 1d5b11a..44fe08c 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,10 @@ CLOUDINARY_CLOUD_NAME= CLOUDINARY_API_KEY= CLOUDINARY_API_SECRET= CLOUDINARY_SECURE=true +SMTP_HOST=localhost +SMTP_PORT=587 +SMTP_USERNAME= +SMTP_PASSWORD= +MAILGUN_API_KEY= +MAILGUN_DOMAIN= +CACHE_VIEWS=false diff --git a/ace-manifest.json b/ace-manifest.json index cd103e9..b53470f 100644 --- a/ace-manifest.json +++ b/ace-manifest.json @@ -2,7 +2,7 @@ "commands": { "dump:rcfile": { "settings": {}, - "commandPath": "@adonisjs/core/commands/DumpRc", + "commandPath": "@adonisjs/core/build/commands/DumpRc", "commandName": "dump:rcfile", "description": "Dump contents of .adonisrc.json file along with defaults", "args": [], @@ -13,7 +13,7 @@ "settings": { "loadApp": true }, - "commandPath": "@adonisjs/core/commands/ListRoutes", + "commandPath": "@adonisjs/core/build/commands/ListRoutes", "commandName": "list:routes", "description": "List application routes", "args": [], @@ -29,7 +29,7 @@ }, "generate:key": { "settings": {}, - "commandPath": "@adonisjs/core/commands/GenerateKey", + "commandPath": "@adonisjs/core/build/commands/GenerateKey", "commandName": "generate:key", "description": "Generate a new APP_KEY secret", "args": [], @@ -261,6 +261,23 @@ "alias": "c" } ] + }, + "make:mailer": { + "settings": {}, + "commandPath": "@adonisjs/mail/build/commands/MakeMailer", + "commandName": "make:mailer", + "description": "Make a new mailer class", + "args": [ + { + "type": "string", + "propertyName": "name", + "name": "name", + "required": true, + "description": "Name of the mailer class" + } + ], + "aliases": [], + "flags": [] } }, "aliases": {} diff --git a/app/Controllers/Http/Api/v1/MeController.ts b/app/Controllers/Http/Api/v1/MeController.ts index a2ed735..a2fec0f 100644 --- a/app/Controllers/Http/Api/v1/MeController.ts +++ b/app/Controllers/Http/Api/v1/MeController.ts @@ -1,5 +1,6 @@ import { Exception, inject, Ioc } from '@adonisjs/core/build/standalone'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; +import { schema, rules } from '@ioc:Adonis/Core/Validator'; /** * Services @@ -52,6 +53,46 @@ export default class MeController extends BaseController { return this.sendResponse(ctx, result.data, result.message, result.status); } + /** + * Send confirmation code to new email + * PATCH /me/email + */ + // eslint-disable-next-line class-methods-use-this + public async changeEmail(ctx: HttpContextContract) { + const payload = await ctx.request.validate({ + schema: schema.create({ + email: schema.string({}, [rules.email()]), + }), + }); + const result = await MeService.changeUserEmail(payload.email); + + if (!result.success && result.error) { + throw new Exception(result.message, result.status, result.error.code); + } + + return this.sendResponse(ctx, result.data, result.message, result.status); + } + + /** + * Confirm code and update user email + * POST /me/email/change/confirm + */ + // eslint-disable-next-line class-methods-use-this + public async changeEmailConfirm(ctx: HttpContextContract) { + const confirmChangeEmailSchema = schema.create({ + email: schema.string({}, [rules.email()]), + confirmationCode: schema.number(), + }); + const payload = await ctx.request.validate({ schema: confirmChangeEmailSchema }); + const result = await MeService.changeUserEmailConfirm(ctx, payload.email, payload.confirmationCode); + + if (!result.success && result.error) { + throw new Exception(result.message, result.status, result.error.code); + } + + return this.sendResponse(ctx, result.data, result.message, result.status); + } + /** * Update contacts of authenticated user * PUT /me/contacts diff --git a/app/Datatypes/Enums/StatusCodeEnum.ts b/app/Datatypes/Enums/StatusCodeEnum.ts index 79db513..63241fd 100644 --- a/app/Datatypes/Enums/StatusCodeEnum.ts +++ b/app/Datatypes/Enums/StatusCodeEnum.ts @@ -7,6 +7,7 @@ enum StatusCodeEnum { FORBIDDEN = 403, NOT_FOUND = 404, INTERNAL_SERVER = 500, + SERVICE_UNAVAILABLE = 503, } export default StatusCodeEnum; diff --git a/app/Services/MeService.ts b/app/Services/MeService.ts index 0714883..8e61f34 100644 --- a/app/Services/MeService.ts +++ b/app/Services/MeService.ts @@ -1,5 +1,8 @@ import { inject, Ioc } from '@adonisjs/core/build/standalone'; import { AuthContract } from '@ioc:Adonis/Addons/Auth'; +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; +import Redis from '@ioc:Adonis/Addons/Redis'; +import Mail from '@ioc:Adonis/Addons/Mail'; import Hash from '@ioc:Adonis/Core/Hash'; import Logger from '@ioc:Adonis/Core/Logger'; import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum'; @@ -95,6 +98,98 @@ export default class MeService { }; } + public static async changeUserEmail(email: string): Promise { + const isConfirmationCodeExist = await Redis.get(`code.change.email:${email}`); + + /** + * Confirmation code has not expired + */ + if (isConfirmationCodeExist) { + return { + success: false, + status: StatusCodeEnum.SERVICE_UNAVAILABLE, + message: 'Please wait a few seconds before send new confirmation code.', + data: {}, + error: { + code: 'E_SERVICE_UNAVAILABLE', + }, + }; + } + + /** + * Create new confirmation code and send to email + */ + const EXPIRE_SECONDS = 60; + const confirmationCode = Math.floor(1000 + Math.random() * 9000).toString(); + + await Mail.send(message => { + message + .from('learn-management-system@sandbox7727dde9c8aa4686a308dbb9c0045acb.mailgun.org') + .to(email) + .subject('Confirmation code.') + .htmlView('emails/change_email_confirm', { confirmationCode }); + }); + await Redis.set(`code.change.email:${email}`, confirmationCode, 'EX', EXPIRE_SECONDS); + + return { + success: true, + status: StatusCodeEnum.OK, + message: 'The confirmation code has been sent.', + data: { + expired_seconds: EXPIRE_SECONDS, + }, + }; + } + + public static async changeUserEmailConfirm( + ctx: HttpContextContract, + email: string, + code: number + ): Promise { + const user = await ctx.auth.use('api').authenticate(); + const confirmationCode = await Redis.get(`code.change.email:${email}`); + + /** + * Confirmation code not found + */ + if (confirmationCode === null) { + return { + success: false, + status: StatusCodeEnum.NOT_FOUND, + message: 'Cannot find the confirmation code for this email.', + data: {}, + error: { + code: 'E_NOT_FOUND', + }, + }; + } + + if (Number.parseInt(confirmationCode, 10) !== code) { + return { + success: false, + status: StatusCodeEnum.BAD_REQUEST, + message: 'Confirmation code is invalid.', + data: {}, + error: { + code: 'E_BAD_REQUEST', + }, + }; + } + + /** + * Update user email and delete confirmation code + */ + await user.merge({ email }).save(); + await Redis.del(`code.change.email:${email}`); + + return { + success: true, + status: StatusCodeEnum.OK, + message: 'User email updated.', + data: { email }, + }; + } + /** * Update user conctact * diff --git a/config/mail.ts b/config/mail.ts new file mode 100644 index 0000000..440345c --- /dev/null +++ b/config/mail.ts @@ -0,0 +1,76 @@ +/** + * Config source: https://git.io/JvgAf + * + * Feel free to let us know via PR, if you find something broken in this contract + * file. + */ + +import Env from '@ioc:Adonis/Core/Env'; +import { MailConfig } from '@ioc:Adonis/Addons/Mail'; + +const mailConfig: MailConfig = { + /* + |-------------------------------------------------------------------------- + | Default mailer + |-------------------------------------------------------------------------- + | + | The following mailer will be used to send emails, when you don't specify + | a mailer + | + */ + mailer: 'mailgun', + + /* + |-------------------------------------------------------------------------- + | Mailers + |-------------------------------------------------------------------------- + | + | You can define or more mailers to send emails from your application. A + | single `driver` can be used to define multiple mailers with different + | config. + | + | For example: Postmark driver can be used to have different mailers for + | sending transactional and promotional emails + | + */ + mailers: { + /* + |-------------------------------------------------------------------------- + | Smtp + |-------------------------------------------------------------------------- + | + | Uses SMTP protocol for sending email + | + */ + smtp: { + driver: 'smtp', + host: Env.get('SMTP_HOST'), + port: Env.get('SMTP_PORT'), + auth: { + user: Env.get('SMTP_USERNAME'), + pass: Env.get('SMTP_PASSWORD'), + type: 'login', + }, + }, + + /* + |-------------------------------------------------------------------------- + | Mailgun + |-------------------------------------------------------------------------- + | + | Uses Mailgun service for sending emails. + | + | If you are using an EU domain. Ensure to change the baseUrl to hit the + | europe endpoint (https://api.eu.mailgun.net/v3). + | + */ + mailgun: { + driver: 'mailgun', + baseUrl: 'https://api.mailgun.net/v3', + key: Env.get('MAILGUN_API_KEY'), + domain: Env.get('MAILGUN_DOMAIN'), + }, + }, +}; + +export default mailConfig; diff --git a/contracts/mail.ts b/contracts/mail.ts new file mode 100644 index 0000000..ee99744 --- /dev/null +++ b/contracts/mail.ts @@ -0,0 +1,15 @@ +/** + * Contract source: https://git.io/JvgAT + * + * Feel free to let us know via PR, if you find something broken in this contract + * file. + */ + +declare module '@ioc:Adonis/Addons/Mail' { + import { MailDrivers } from '@ioc:Adonis/Addons/Mail'; + + interface MailersList { + smtp: MailDrivers['smtp']; + mailgun: MailDrivers['mailgun']; + } +} diff --git a/env.ts b/env.ts index 81eb63e..fc14b74 100644 --- a/env.ts +++ b/env.ts @@ -30,6 +30,12 @@ export default Env.rules({ REDIS_HOST: Env.schema.string({ format: 'host' }), REDIS_PORT: Env.schema.number(), REDIS_PASSWORD: Env.schema.string.optional(), + SMTP_HOST: Env.schema.string({ format: 'host' }), + SMTP_PORT: Env.schema.number(), + SMTP_USERNAME: Env.schema.string(), + SMTP_PASSWORD: Env.schema.string(), + MAILGUN_API_KEY: Env.schema.string(), + MAILGUN_DOMAIN: Env.schema.string(), CLOUDINARY_CLOUD_NAME: Env.schema.string(), CLOUDINARY_API_KEY: Env.schema.string(), CLOUDINARY_API_SECRET: Env.schema.string(), diff --git a/package-lock.json b/package-lock.json index 692fae9..86ba9ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -674,6 +674,52 @@ "slash": "^3.0.0" } }, + "@adonisjs/mail": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@adonisjs/mail/-/mail-7.2.4.tgz", + "integrity": "sha512-ddoeT5rGV1JgLjMmOIFxzFkWFDc1gb59d+CQ45DNOyDboSEkSvKt5O2LBnBuTKMBjFUMUjvnYDjkb7QgbFN2fw==", + "requires": { + "@poppinss/colors": "^2.1.5", + "@poppinss/manager": "^4.0.4", + "@poppinss/utils": "^3.2.0", + "fastq": "^1.12.0", + "get-stream": "^6.0.1", + "got": "^11.8.2", + "ical-generator": "^3.0.0", + "multi-part": "^3.0.0", + "nodemailer": "^6.6.3" + }, + "dependencies": { + "@poppinss/utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@poppinss/utils/-/utils-3.2.0.tgz", + "integrity": "sha512-gnqlrxE4zMvqW+LpviyCWCg4+RShCYCFyBdVTyg9ZZEEgPBV9bEY+YB5r/2klFrKx30k9nfClHVI2n8ZKmXgsw==", + "requires": { + "@types/bytes": "^3.1.1", + "@types/he": "^1.1.2", + "buffer-alloc": "^1.2.0", + "bytes": "^3.1.0", + "change-case": "^4.1.2", + "cuid": "^2.1.8", + "fs-readdir-recursive": "^1.1.0", + "he": "^1.2.0", + "kind-of": "^6.0.3", + "lodash": "^4.17.21", + "ms": "^2.1.3", + "pluralize": "^8.0.0", + "require-all": "^3.0.0", + "resolve-from": "^5.0.0", + "slugify": "^1.6.0", + "truncatise": "0.0.8" + } + }, + "slugify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.0.tgz", + "integrity": "sha512-FkMq+MQc5hzYgM86nLuHI98Acwi3p4wX+a5BO9Hhw4JdK4L7WueIiZ4tXEobImPqBz2sVcV0+Mu3GRB30IGang==" + } + } + }, "@adonisjs/profiler": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/@adonisjs/profiler/-/profiler-6.0.6.tgz", @@ -911,6 +957,16 @@ } } }, + "@adonisjs/view": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@adonisjs/view/-/view-6.1.0.tgz", + "integrity": "sha512-6odN0FLSM4+0zVOQ14BTXBymDsCRrI95yGbAOUex6hMeSvBBff6f+VlH+WsiPS7xyKewI7ihZx6yRsVERBb0bw==", + "requires": { + "edge-error": "^2.0.5", + "edge-supercharged": "^3.1.1", + "edge.js": "^5.3.1" + } + }, "@arr/every": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@arr/every/-/every-1.0.1.tgz", @@ -1173,6 +1229,11 @@ "resolved": "https://registry.npmjs.org/@poppinss/hooks/-/hooks-3.0.4.tgz", "integrity": "sha512-vVtd8v3grQ0JcbZgvUrUIUeMqTcHkE31nJGE/jRxDEbCiusBwBJD6D16a5Wzbl+o6xRldlhIcdDn+AE8PUpuVA==" }, + "@poppinss/inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@poppinss/inspect/-/inspect-1.0.1.tgz", + "integrity": "sha512-kLeEaBSGhlleyYvKc7c9s3uE6xv7cwyulE0EgHf4jU/CL96h0yC4mkdw1wvC1l1PYYQozCGy46FwMBAAMOobCA==" + }, "@poppinss/manager": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@poppinss/manager/-/manager-4.0.4.tgz", @@ -1248,6 +1309,19 @@ "truncatise": "0.0.8" } }, + "@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, "@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -1258,6 +1332,17 @@ "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==" }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/faker": { "version": "5.5.7", "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.7.tgz", @@ -1286,6 +1371,11 @@ "resolved": "https://registry.npmjs.org/@types/he/-/he-1.1.2.tgz", "integrity": "sha512-kSJPcLO1x+oolc0R89pUl2kozldQ/fVQ1C1p5mp8fPoLdF/ZcBvckaTC2M8xXh3GYendXvCpy5m/a2eSbfgNgw==" }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, "@types/ioredis": { "version": "4.27.1", "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.27.1.tgz", @@ -1306,6 +1396,14 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/keyv": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", + "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "requires": { + "@types/node": "*" + } + }, "@types/luxon": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-2.0.2.tgz", @@ -1349,6 +1447,14 @@ "@types/node": "*" } }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/stack-trace": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", @@ -1858,6 +1964,11 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, + "astring": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.7.5.tgz", + "integrity": "sha512-lobf6RWXb8c4uZ7Mdq0U12efYmpD1UFnyOWVJPTa3ukqZrMopav+2hdNu0hgBF0JIBFK9QgrBDfwYvh3DFJDAA==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -2078,6 +2189,35 @@ "unset-value": "^1.0.0" } }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -2264,6 +2404,14 @@ "string-width": "^4.2.0" } }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "cloudinary": { "version": "1.26.3", "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-1.26.3.tgz", @@ -2512,6 +2660,21 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2524,6 +2687,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -2643,6 +2811,90 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" }, + "edge-error": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/edge-error/-/edge-error-2.0.6.tgz", + "integrity": "sha512-hXJDgkAVPypbPrwc0JSb+SOgchk9fKp7ZRF43ikM1L/XqhKcxX5oh1ilLQeC/UtBBLm85Xjb1887HQBJD+bxVw==" + }, + "edge-lexer": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/edge-lexer/-/edge-lexer-4.0.8.tgz", + "integrity": "sha512-5DnQ6wy8Ebd3P/2XtwQryCeKgZrMEmxWz12YdjOhQb+UNq6OeXEY5AC6zaSYFpPlGCBCUB+Mz9U6ja2lkh20+Q==", + "requires": { + "edge-error": "^2.0.6" + } + }, + "edge-parser": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-8.0.11.tgz", + "integrity": "sha512-W3UDF9xWAMs+v7QE3J0QT5azqYWxwqUedWKWLTT1siEfl2Upg/zFE3IZ5PPOvXipFRZvW75QNxbhfK09+WrpZg==", + "requires": { + "acorn": "^8.4.1", + "astring": "^1.7.5", + "edge-error": "^2.0.6", + "edge-lexer": "^4.0.8", + "js-stringify": "^1.0.2" + } + }, + "edge-supercharged": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/edge-supercharged/-/edge-supercharged-3.1.1.tgz", + "integrity": "sha512-Pxgst3UeR+0PWuTIG2QHuFgFe73TMoVMphRsa/bOmIvOkQ87cD7+VxiS9ph2KbA4djJ2ChbdgtbNiIvju5yXEw==", + "requires": { + "@poppinss/utils": "^3.1.3", + "slash": "^3.0.0" + } + }, + "edge.js": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/edge.js/-/edge.js-5.3.1.tgz", + "integrity": "sha512-TWiPo7Unaww1fYll/zMANVma08q2WoG8j7g9iK3fed5AevD0XuBl8N2L9+vCE7TKJmTm/zNP5z8bKbfGWOTRbw==", + "requires": { + "@poppinss/inspect": "^1.0.1", + "@poppinss/utils": "^3.1.4", + "edge-error": "^2.0.5", + "edge-lexer": "^4.0.7", + "edge-parser": "^8.0.10", + "js-stringify": "^1.0.2", + "macroable": "^5.1.4", + "stringify-attributes": "^2.0.0" + }, + "dependencies": { + "@poppinss/utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@poppinss/utils/-/utils-3.2.0.tgz", + "integrity": "sha512-gnqlrxE4zMvqW+LpviyCWCg4+RShCYCFyBdVTyg9ZZEEgPBV9bEY+YB5r/2klFrKx30k9nfClHVI2n8ZKmXgsw==", + "requires": { + "@types/bytes": "^3.1.1", + "@types/he": "^1.1.2", + "buffer-alloc": "^1.2.0", + "bytes": "^3.1.0", + "change-case": "^4.1.2", + "cuid": "^2.1.8", + "fs-readdir-recursive": "^1.1.0", + "he": "^1.2.0", + "kind-of": "^6.0.3", + "lodash": "^4.17.21", + "ms": "^2.1.3", + "pluralize": "^8.0.0", + "require-all": "^3.0.0", + "resolve-from": "^5.0.0", + "slugify": "^1.6.0", + "truncatise": "0.0.8" + } + }, + "macroable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/macroable/-/macroable-5.1.4.tgz", + "integrity": "sha512-6yOJQT7QhYlJqhNWn/B2h+1ylsCnL8dMBmBpdnVNK+02PfQm13Ix4Szx4HiS3rp4a28EKckCGW27gtTZoYXYYQ==" + }, + "slugify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.0.tgz", + "integrity": "sha512-FkMq+MQc5hzYgM86nLuHI98Acwi3p4wX+a5BO9Hhw4JdK4L7WueIiZ4tXEobImPqBz2sVcV0+Mu3GRB30IGang==" + } + } + }, "editorconfig": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", @@ -3474,7 +3726,6 @@ "version": "1.12.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -3692,8 +3943,7 @@ "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" }, "get-value": { "version": "2.0.6", @@ -3775,6 +4025,24 @@ } } }, + "got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -3935,6 +4203,11 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "http-errors": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", @@ -3947,6 +4220,15 @@ "toidentifier": "1.0.0" } }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -3962,6 +4244,14 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "ical-generator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-3.0.0.tgz", + "integrity": "sha512-PlDOOipgTlMzbfDDy+NJixAf5bzNb46wf0JXPkhnSjNQJc50y/XTOhOnQW/cp6tD+4ITHIMhkCkQ4CrUlDKMFQ==", + "requires": { + "uuid-random": "^1.3.2" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4335,6 +4625,11 @@ "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==", "dev": true }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -4351,6 +4646,11 @@ "esprima": "^4.0.0" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -4393,6 +4693,14 @@ "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", "dev": true }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -4570,6 +4878,11 @@ "tslib": "^2.0.3" } }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4804,6 +5117,22 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" }, + "mime-kind": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime-kind/-/mime-kind-3.0.0.tgz", + "integrity": "sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ==", + "requires": { + "file-type": "^12.1.0", + "mime-types": "^2.1.24" + }, + "dependencies": { + "file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==" + } + } + }, "mime-types": { "version": "2.1.32", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", @@ -4817,6 +5146,11 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4953,6 +5287,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "multi-part": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multi-part/-/multi-part-3.0.0.tgz", + "integrity": "sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw==", + "requires": { + "mime-kind": "^3.0.0", + "multi-part-lite": "^1.0.0" + } + }, + "multi-part-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz", + "integrity": "sha512-KxIRbBZZ45hoKX1ROD/19wJr0ql1bef1rE8Y1PCwD3PuNXV42pp7Wo8lEHYuAajoT4vfAFcd3rPjlkyEEyt1nw==" + }, "mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -5045,6 +5393,11 @@ "acorn-walk": "^8.0.2" } }, + "nodemailer": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz", + "integrity": "sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew==" + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -5274,6 +5627,11 @@ } } }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -5794,7 +6152,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -5830,6 +6187,11 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz", "integrity": "sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg==" }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -6059,6 +6421,11 @@ "path-parse": "^1.0.6" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -6070,6 +6437,14 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -6088,8 +6463,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rev-hash": { "version": "3.0.0", @@ -7047,6 +7421,11 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "uuid-random": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.2.tgz", + "integrity": "sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index e6d9c42..a65b8c9 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,10 @@ "@adonisjs/core": "^5.3.2", "@adonisjs/drive": "^2.0.6", "@adonisjs/lucid": "^15.0.3", + "@adonisjs/mail": "^7.2.4", "@adonisjs/redis": "^7.0.9", "@adonisjs/repl": "^3.1.6", + "@adonisjs/view": "^6.1.0", "cloudinary": "^1.26.3", "luxon": "^1.28.0", "nanoid": "^3.1.25", diff --git a/resources/views/emails/change_email_confirm.edge b/resources/views/emails/change_email_confirm.edge new file mode 100644 index 0000000..b01f225 --- /dev/null +++ b/resources/views/emails/change_email_confirm.edge @@ -0,0 +1,4 @@ +

Your confirmation code for changing email

+

+ Code: {{ confirmationCode }} +

\ No newline at end of file diff --git a/routes/apis/v1/me.ts b/routes/apis/v1/me.ts index 15b52a1..25a6387 100644 --- a/routes/apis/v1/me.ts +++ b/routes/apis/v1/me.ts @@ -2,6 +2,12 @@ import Route from '@ioc:Adonis/Core/Route'; Route.group(() => { Route.get('/', 'Api/v1/MeController.show').middleware('role:admin,teacher,student').as('me.show'); + Route.patch('/email', 'Api/v1/MeController.changeEmail') + .middleware('role:admin,teacher,student') + .as('me.change-email'); + Route.post('/email/change/confirm', 'Api/v1/MeController.changeEmailConfirm') + .middleware('role:admin,teacher,student') + .as('me.change-email-confirm'); Route.patch('/password', 'Api/v1/MeController.changePassword') .middleware('role:admin,teacher,student') .as('me.change-password'); diff --git a/tsconfig.json b/tsconfig.json index c2fb893..5ae061e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,39 @@ { "extends": "./node_modules/adonis-preset-ts/tsconfig", - "include": ["**/*"], - "exclude": ["node_modules", "build"], + "include": [ + "**/*" + ], + "exclude": [ + "node_modules", + "build" + ], "compilerOptions": { "outDir": "build", "rootDir": "./", "sourceMap": true, "useDefineForClassFields": true, "paths": { - "App/*": ["./app/*"], - "Config/*": ["./config/*"], - "Contracts/*": ["./contracts/*"], - "Database/*": ["./database/*"] + "App/*": [ + "./app/*" + ], + "Config/*": [ + "./config/*" + ], + "Contracts/*": [ + "./contracts/*" + ], + "Database/*": [ + "./database/*" + ] }, - "types": ["@adonisjs/core", "@adonisjs/repl", "@adonisjs/lucid", "@adonisjs/auth", "@adonisjs/redis"] + "types": [ + "@adonisjs/core", + "@adonisjs/repl", + "@adonisjs/lucid", + "@adonisjs/auth", + "@adonisjs/redis", + "@adonisjs/mail", + "@adonisjs/view" + ] } }