From d8d6c922c1ed0e8d53ead26c3399bdbc213c86f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Thu, 16 Nov 2023 00:44:20 +0300 Subject: [PATCH 01/14] Update user schema --- prisma/schema.prisma | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 61d25c6..d04cd48 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -12,7 +12,9 @@ datasource db { model User { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String + password String email String @unique + role Role @default(USER) createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") @@map("users") @@ -25,4 +27,9 @@ model Post { createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") @@map("posts") +} + +enum Role { + USER + ADMIN } \ No newline at end of file From ea2ad612c152631486101acb1699d6eaad70c4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Thu, 16 Nov 2023 21:26:19 +0300 Subject: [PATCH 02/14] Add cookie service --- package-lock.json | 29 +++++++++++++++++++++++++++++ package.json | 1 + src/service/cookie.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/service/cookie.ts diff --git a/package-lock.json b/package-lock.json index 54b77d5..66f2b4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@prisma/client": "5.5.2", "clsx": "2.0.0", + "cookies-next": "4.0.0", "joi": "17.11.0", "next": "14.0.1", "next-connect": "1.0.0", @@ -1581,6 +1582,11 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2821,6 +2827,29 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookies-next": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-4.0.0.tgz", + "integrity": "sha512-3TyzeltFCGgdOlVOVTPClSq+YV9ZCdOyA3aHRZv9f5aSgg7EyI4NSvXFOCgzT/xIxeHR4Rz8/z5Tdo9oPqaVpA==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/node": "^16.10.2", + "cookie": "^0.4.0" + } + }, + "node_modules/cookies-next/node_modules/@types/node": { + "version": "16.18.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.61.tgz", + "integrity": "sha512-k0N7BqGhJoJzdh6MuQg1V1ragJiXTh8VUBAZTWjJ9cUq23SG0F0xavOwZbhiP4J3y20xd6jxKx+xNUhkMAi76Q==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", diff --git a/package.json b/package.json index a999fd4..6b98aeb 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "@prisma/client": "5.5.2", "clsx": "2.0.0", + "cookies-next": "4.0.0", "joi": "17.11.0", "next": "14.0.1", "next-connect": "1.0.0", diff --git a/src/service/cookie.ts b/src/service/cookie.ts new file mode 100644 index 0000000..35c488b --- /dev/null +++ b/src/service/cookie.ts @@ -0,0 +1,41 @@ +import {IncomingMessage, ServerResponse} from "http"; +import {deleteCookie, getCookie, setCookie} from "cookies-next"; +import {OptionsType} from "cookies-next/lib/types"; + +type FrontOptions = Omit +type ServerOptionsGet = OptionsType & { + req: IncomingMessage & { + cookies?: {[key: string]: string} | Partial<{[key: string]: string}> + }}; +type ServerOptionsSet = ServerOptionsGet & { + res: ServerResponse; +} + +const CookieFront = { + get: (key: string, options?: FrontOptions) => { + return getCookie(key, options); + }, + set: (key: string, data: any, options?: FrontOptions) => { + return setCookie(key, data, options); + }, + delete: (key: string, options?: FrontOptions) => { + return deleteCookie(key, options); + }, +}; + +const CookieServer = { + get: (key: string, options: ServerOptionsGet) => { + return getCookie(key, options); + }, + set: (key: string, data: any, options: ServerOptionsSet) => { + return setCookie(key, data, options); + }, + delete: (key: string, options: ServerOptionsSet) => { + return deleteCookie(key, options); + } +}; + +export { + CookieFront, + CookieServer, +}; From e4b708757e13a39d6794b5437d5693ae27fc8c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Thu, 16 Nov 2023 21:40:38 +0300 Subject: [PATCH 03/14] Add token service --- config.js | 4 +- package-lock.json | 123 +++++++++++++++++++++++++++++++++++++++++-- package.json | 2 + src/service/token.ts | 23 ++++++++ 4 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 src/service/token.ts diff --git a/config.js b/config.js index 8993606..b4b4214 100644 --- a/config.js +++ b/config.js @@ -6,12 +6,15 @@ const PUSHER_SECRET = String(process.env.PUSHER_SECRET || ``); const PUSHER_CLUSTER = String(process.env.NEXT_PUBLIC_PUSHER_CLUSTER || ``); const TELEGRAM_AUTH_STRING = String(process.env.TELEGRAM_AUTH_STRING); const POSTGRES_CONNECTION_URL = String(process.env.POSTGRES_CONNECTION_URL || ``); +const JWT_ACCESS_KEY = String(process.env.JWT_ACCESS_KEY || ``); // Public const SITE_URL = String(process.env.NEXT_PUBLIC_SITE_URL || ``); const PUSHER_KEY = String(process.env.NEXT_PUBLIC_PUSHER_KEY || ``); module.exports = { + JWT_ACCESS_KEY, + POSTGRES_CONNECTION_URL, PUSHER_APP_ID, PUSHER_CLUSTER, PUSHER_KEY, @@ -20,5 +23,4 @@ module.exports = { TELEGRAM_API_HASH, TELEGRAM_API_ID, TELEGRAM_AUTH_STRING, - POSTGRES_CONNECTION_URL, }; diff --git a/package-lock.json b/package-lock.json index 66f2b4b..e6fcff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "clsx": "2.0.0", "cookies-next": "4.0.0", "joi": "17.11.0", + "jsonwebtoken": "9.0.2", "next": "14.0.1", "next-connect": "1.0.0", "pusher": "5.1.3", @@ -27,6 +28,7 @@ }, "devDependencies": { "@types/joi": "17.2.3", + "@types/jsonwebtoken": "9.0.5", "@types/react": "18.2.34", "@types/react-dom": "18.2.14", "@typescript-eslint/eslint-plugin": "5.62.0", @@ -1658,6 +1660,15 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mdast": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", @@ -2578,6 +2589,11 @@ "ieee754": "^1.2.1" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3095,6 +3111,14 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.570", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.570.tgz", @@ -6094,6 +6118,27 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -6109,6 +6154,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -6164,12 +6228,47 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -6194,7 +6293,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -8092,6 +8190,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -8118,7 +8235,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9269,8 +9385,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "17.6.2", diff --git a/package.json b/package.json index 6b98aeb..d174d9c 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "clsx": "2.0.0", "cookies-next": "4.0.0", "joi": "17.11.0", + "jsonwebtoken": "9.0.2", "next": "14.0.1", "next-connect": "1.0.0", "pusher": "5.1.3", @@ -33,6 +34,7 @@ }, "devDependencies": { "@types/joi": "17.2.3", + "@types/jsonwebtoken": "9.0.5", "@types/react": "18.2.34", "@types/react-dom": "18.2.14", "@typescript-eslint/eslint-plugin": "5.62.0", diff --git a/src/service/token.ts b/src/service/token.ts new file mode 100644 index 0000000..f8418e3 --- /dev/null +++ b/src/service/token.ts @@ -0,0 +1,23 @@ +import jwt, {JwtPayload} from "jsonwebtoken"; +import {JWT_ACCESS_KEY} from "../../config"; + +if (!JWT_ACCESS_KEY) { + throw new Error(`Token service: missed JWT_ACCESS_KEY`); +} + +const generateToken = (data: string | object | Buffer, expiresInSeconds: number): string => { + return jwt.sign(data, JWT_ACCESS_KEY as string, {expiresIn: expiresInSeconds}); +}; + +const validateToken = (token: string): string | JwtPayload | null => { + try { + return jwt.verify(token, JWT_ACCESS_KEY as string); + } catch (err) { + return null; + } +}; + +export { + generateToken, + validateToken, +}; From 9745fbd8d87562f028c838d7cf530a1ec1d4b639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 18 Nov 2023 14:00:05 +0300 Subject: [PATCH 04/14] Add session service --- prisma/schema.prisma | 8 +++ src/controllers/session/session.service.ts | 66 ++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/controllers/session/session.service.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d04cd48..f284505 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -17,6 +17,7 @@ model User { role Role @default(USER) createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") + session Session? @@map("users") } @@ -29,6 +30,13 @@ model Post { @@map("posts") } +model Session { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.Uuid + refreshToken String +} + enum Role { USER ADMIN diff --git a/src/controllers/session/session.service.ts b/src/controllers/session/session.service.ts new file mode 100644 index 0000000..834169c --- /dev/null +++ b/src/controllers/session/session.service.ts @@ -0,0 +1,66 @@ +import {ApiError} from "../../service/api/error"; +import {prisma} from "../../service/prisma-client"; +import {generateToken} from "../../service/token"; +import {handlePromise, log} from "../../utils/utils"; + +type TTokens = { + accessToken: string, + refreshToken: string, +} + +const ACCESS_TOKEN_AGE_IN_SEC = 30 * 60; +const REFRESH_TOKEN_AGE_IN_SEC = 30 * 24 * 60 * 60; + +const sessionService = { + generateTokens(data: string | object | Buffer): TTokens { + const accessToken = generateToken(data, ACCESS_TOKEN_AGE_IN_SEC); + const refreshToken = generateToken(data, REFRESH_TOKEN_AGE_IN_SEC); + + return {accessToken, refreshToken}; + }, + + async saveTokens(userId: string, refreshToken: string) { + const [sessionFindingError, existedSession] = await handlePromise(prisma.user.findFirst({ + where: {id: userId}, + }).session()); + + if (sessionFindingError) { + log(`Session Service saveTokens:`, sessionFindingError); + throw ApiError.internalServerError(); + } + + if (existedSession) { + const [sessionUpdatingError, session] = await handlePromise(prisma.session.update({ + where: {userId}, + data: { + refreshToken, + }, + })); + + if (sessionUpdatingError) { + log(`Session Service saveTokens:`, sessionFindingError); + throw ApiError.internalServerError(); + } + + return session; + } + + const [sessionCreatingError, session] = await handlePromise(prisma.session.create({ + data: { + userId, + refreshToken, + } + })); + + if (sessionCreatingError) { + log(`Session Service saveTokens:`, sessionFindingError); + throw ApiError.internalServerError(); + } + + return session; + } +}; + +export { + sessionService, +}; From e8d2431044d4ae834388920b65106c6b839666d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 18 Nov 2023 17:14:20 +0300 Subject: [PATCH 05/14] Add user service(WIP) --- package-lock.json | 352 +++++++++++++++++++-- package.json | 6 +- prisma/schema.prisma | 12 +- src/controllers/session/session.service.ts | 39 +-- src/controllers/user/user.service.ts | 88 ++++++ 5 files changed, 435 insertions(+), 62 deletions(-) create mode 100644 src/controllers/user/user.service.ts diff --git a/package-lock.json b/package-lock.json index e6fcff0..a438edd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@prisma/client": "5.5.2", + "bcrypt": "5.1.1", "clsx": "2.0.0", "cookies-next": "4.0.0", "joi": "17.11.0", @@ -24,13 +25,16 @@ "remark-parse": "11.0.0", "telegram": "2.19.8", "typescript": "4.9.3", - "unified": "11.0.4" + "unified": "11.0.4", + "uuid": "9.0.1" }, "devDependencies": { + "@types/bcrypt": "5.0.2", "@types/joi": "17.2.3", "@types/jsonwebtoken": "9.0.5", "@types/react": "18.2.34", "@types/react-dom": "18.2.14", + "@types/uuid": "9.0.7", "@typescript-eslint/eslint-plugin": "5.62.0", "eslint": "8.28.0", "eslint-config-htmlacademy": "8.0.0", @@ -1226,6 +1230,25 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@next/env": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.1.tgz", @@ -1584,6 +1607,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -1751,6 +1783,12 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" }, + "node_modules/@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.14.tgz", @@ -2055,6 +2093,11 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -2087,6 +2130,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2134,7 +2188,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2167,6 +2220,23 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2473,8 +2543,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -2495,6 +2564,19 @@ } ] }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -2507,7 +2589,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2735,6 +2816,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", @@ -2811,6 +2900,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2834,8 +2931,12 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -2998,6 +3099,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3006,6 +3112,14 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4281,11 +4395,32 @@ "node": ">= 6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", @@ -4337,6 +4472,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4420,7 +4574,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4625,6 +4778,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -4808,6 +4966,18 @@ "entities": "^2.0.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4892,7 +5062,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -4901,8 +5070,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.6", @@ -5074,7 +5242,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -6304,7 +6471,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "dependencies": { "semver": "^6.0.0" }, @@ -6319,7 +6485,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -7155,7 +7320,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7172,6 +7336,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7268,6 +7474,11 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7330,6 +7541,20 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7351,11 +7576,21 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7473,7 +7708,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -7593,7 +7827,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7929,6 +8162,19 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/real-cancellable-promise": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/real-cancellable-promise/-/real-cancellable-promise-1.2.0.tgz", @@ -8138,7 +8384,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -8245,6 +8490,11 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -8312,8 +8562,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -8436,6 +8685,14 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -8453,7 +8710,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8466,8 +8722,7 @@ "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string.prototype.matchall": { "version": "4.0.8", @@ -8550,7 +8805,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8659,6 +8913,22 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/telegram": { "version": "2.19.8", "resolved": "https://registry.npmjs.org/telegram/-/telegram-2.19.8.tgz", @@ -9096,6 +9366,23 @@ "node": ">=6.14.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -9320,6 +9607,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -9349,8 +9644,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", diff --git a/package.json b/package.json index d174d9c..fe7436e 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@prisma/client": "5.5.2", + "bcrypt": "5.1.1", "clsx": "2.0.0", "cookies-next": "4.0.0", "joi": "17.11.0", @@ -30,13 +31,16 @@ "remark-parse": "11.0.0", "telegram": "2.19.8", "typescript": "4.9.3", - "unified": "11.0.4" + "unified": "11.0.4", + "uuid": "9.0.1" }, "devDependencies": { + "@types/bcrypt": "5.0.2", "@types/joi": "17.2.3", "@types/jsonwebtoken": "9.0.5", "@types/react": "18.2.34", "@types/react-dom": "18.2.14", + "@types/uuid": "9.0.7", "@typescript-eslint/eslint-plugin": "5.62.0", "eslint": "8.28.0", "eslint-config-htmlacademy": "8.0.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f284505..6645de6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,9 +15,12 @@ model User { password String email String @unique role Role @default(USER) + activationLink String @unique + isActivated Boolean @default(false) + session Session? + createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") - session Session? @@map("users") } @@ -25,6 +28,7 @@ model Post { id Int @id @default(autoincrement()) title String content String + createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") @@map("posts") @@ -34,7 +38,11 @@ model Session { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid user User @relation(fields: [userId], references: [id]) userId String @unique @db.Uuid - refreshToken String + refreshToken String? + + createdAt DateTime @default(now()) @map(name: "created_at") + updatedAt DateTime @updatedAt @map(name: "updated_at") + @@map("sessions") } enum Role { diff --git a/src/controllers/session/session.service.ts b/src/controllers/session/session.service.ts index 834169c..882287b 100644 --- a/src/controllers/session/session.service.ts +++ b/src/controllers/session/session.service.ts @@ -19,41 +19,20 @@ const sessionService = { return {accessToken, refreshToken}; }, - async saveTokens(userId: string, refreshToken: string) { - const [sessionFindingError, existedSession] = await handlePromise(prisma.user.findFirst({ - where: {id: userId}, - }).session()); - - if (sessionFindingError) { - log(`Session Service saveTokens:`, sessionFindingError); - throw ApiError.internalServerError(); - } - - if (existedSession) { - const [sessionUpdatingError, session] = await handlePromise(prisma.session.update({ - where: {userId}, - data: { - refreshToken, - }, - })); - - if (sessionUpdatingError) { - log(`Session Service saveTokens:`, sessionFindingError); - throw ApiError.internalServerError(); - } - - return session; - } - - const [sessionCreatingError, session] = await handlePromise(prisma.session.create({ - data: { + async saveToken(userId: string, refreshToken: string) { + const [sessionError, session] = await handlePromise(prisma.session.upsert({ + where: {userId}, + update: { + refreshToken, + }, + create: { userId, refreshToken, } })); - if (sessionCreatingError) { - log(`Session Service saveTokens:`, sessionFindingError); + if (sessionError) { + log(`Session Service saveTokens:`, sessionError); throw ApiError.internalServerError(); } diff --git a/src/controllers/user/user.service.ts b/src/controllers/user/user.service.ts new file mode 100644 index 0000000..d00cf96 --- /dev/null +++ b/src/controllers/user/user.service.ts @@ -0,0 +1,88 @@ +import bcrypt from "bcrypt"; +import uuid from "uuid"; +import {ApiError} from "../../service/api/error"; +import {prisma} from "../../service/prisma-client"; +import {handlePromise, log} from "../../utils/utils"; + +type TRegistrationData = { + name: string, + email: string, + password: string, +} + +const createActivationLink = () => { + return uuid.v4(); +}; + +const userService = { + async registration(data: TRegistrationData) { + const [findingError, user] = await handlePromise(prisma.user.findFirst({ + where: { + email: data.email.toLowerCase(), + } + })); + + if (findingError) { + log(`User Service Registration`, findingError); + throw ApiError.internalServerError(); + } + + if (user) { + throw ApiError.badRequest(`Пользователь с таким емейлом уже существует`); + } + + const {name, email, password} = data; + const hashingPassword = await bcrypt.hash(password, 5); + const activationLink = createActivationLink(); + + const [creatingError, newUser] = await handlePromise(prisma.user.create({ + data: { + name, + email: email.toLowerCase(), + password: hashingPassword, + isActivated: false, + activationLink, + } + })); + + if (creatingError) { + log(`User Service Registration`, findingError); + throw ApiError.internalServerError(); + } + + return newUser; + }, + + async activateUser(activationLink: string) { + const [findingError, user] = await handlePromise(prisma.user.findUnique({ + where: {activationLink}, + })); + + if (findingError) { + throw ApiError.internalServerError(); + } + + if (!user) { + throw ApiError.badRequest(`Ссылка недействительна`); + } + + if (user.isActivated) { + throw ApiError.badRequest(`Аккаунт уже активирован`); + } + + const [updatingError] = await handlePromise(prisma.user.update({ + where: {id: user.id}, + data: {isActivated: true} + })); + + if (!updatingError) { + throw ApiError.internalServerError(); + } + + return true; + } +}; + +export { + userService, +}; From 415dd2db5ce3f6bb65745d2804851ecf77c8748f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 18 Nov 2023 21:58:56 +0300 Subject: [PATCH 06/14] Add user dto --- src/controllers/user/user.dto.ts | 51 +++++++++++++++++++++++++ src/controllers/user/user.validation.ts | 21 ++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/controllers/user/user.dto.ts create mode 100644 src/controllers/user/user.validation.ts diff --git a/src/controllers/user/user.dto.ts b/src/controllers/user/user.dto.ts new file mode 100644 index 0000000..eb2648e --- /dev/null +++ b/src/controllers/user/user.dto.ts @@ -0,0 +1,51 @@ +import {Prisma, Role} from "@prisma/client"; + +type TUserJSON = { + id: string, + name: string, + email: string, + role: Role, + createdAt: string, + updatedAt: string, +} + +class UserDto { + id: string; + name: string; + email: string; + role: Role; + createdAt: Date; + updatedAt: Date; + activationLink: string; + isActivated: boolean; + + constructor(data: Prisma.UserGetPayload) { + this.id = data.id; + this.name = data.name; + this.role = data.role; + this.email = data.email; + this.createdAt = data.createdAt; + this.updatedAt = data.updatedAt; + this.activationLink = data.activationLink; + this.isActivated = data.isActivated; + } + + toJSON(data: Prisma.UserGetPayload): TUserJSON { + return { + id: data.id, + name: data.name, + role: data.role, + email: data.email, + updatedAt: data.updatedAt.toJSON(), + createdAt: data.createdAt.toJSON(), + }; + } +} + +export type { + TUserJSON, +}; + +export { + UserDto, +}; diff --git a/src/controllers/user/user.validation.ts b/src/controllers/user/user.validation.ts new file mode 100644 index 0000000..5b7cbf6 --- /dev/null +++ b/src/controllers/user/user.validation.ts @@ -0,0 +1,21 @@ +import {ValidationOptions} from "joi"; +import joi from "types-joi"; +import {validate} from "../../service/api/validation"; + +const name = joi.string().alphanum().min(2).required(); +const password = joi.string().alphanum().min(8).required(); +const email = joi.string().email().required(); + +const userValidation = { + registration(data: any, options?: ValidationOptions) { + return validate(joi.object({ + name, + password, + email, + }), data, options); + } +}; + +export { + userValidation, +}; From 1ea4e10f6b2c9c1da6515ab209238e7f77fa30bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 18 Nov 2023 22:46:32 +0300 Subject: [PATCH 07/14] Fix joi locale --- src/service/api/validation.locale.ts | 158 +++++++++++++-------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/src/service/api/validation.locale.ts b/src/service/api/validation.locale.ts index 5b45877..8b655f9 100644 --- a/src/service/api/validation.locale.ts +++ b/src/service/api/validation.locale.ts @@ -2,114 +2,114 @@ // Original english example https://gist.github.com/arifmahmudrana/b991f1c15162654b3a481780884c0d4d const russianLocaleObject = { "any": { - "unknown": `не разрешен`, - "invalid": `содержит недопустимое значение`, - "empty": `не должен быть пустым`, - "required": `является обязательным`, - "allowOnly": `должен быть одним из {{#valids}}`, - "default": `вызвал ошибку при выполнении метода default` + "unknown": `{{#label}} не разрешен`, + "invalid": `{{#label}} содержит недопустимое значение`, + "empty": `{{#label}} не должен быть пустым`, + "required": `{{#label}} является обязательным`, + "allowOnly": `{{#label}} должен быть одним из {{#valids}}`, + "default": `{{#label}} вызвал ошибку при выполнении метода default` }, "alternatives": { - "base": `не соответствует ни одной из допустимых альтернатив` + "base": `{{#label}} не соответствует ни одной из допустимых альтернатив` }, "array": { - "base": `должен быть массивом`, - "includes": `в позиции {{#pos}} не соответствует ни одному из допустимых типов`, + "base": `{{#label}} должен быть массивом`, + "includes": `{{#label}} в позиции {{#pos}} не соответствует ни одному из допустимых типов`, "includesSingle": `единственное значение "{{#label}}" не соответствует ни одному из допустимых типов`, - "includesOne": `в позиции {{#pos}} не проходит проверку, потому что {{#reason}}`, + "includesOne": `{{#label}} в позиции {{#pos}} не проходит проверку, потому что {{#reason}}`, "includesOneSingle": `единственное значение "{{#label}}" не проходит проверку, потому что {{#reason}}`, - "includesRequiredUnknowns": `не содержит {{#unknownMisses}} требуемых значений`, - "includesRequiredKnowns": `не содержит {{#knownMisses}}`, - "includesRequiredBoth": `не содержит {{#knownMisses}} и {{#unknownMisses}} других требуемых значений`, - "excludes": `в позиции {{#pos}} содержит исключенное значение`, + "includesRequiredUnknowns": `{{#label}} не содержит {{#unknownMisses}} требуемых значений`, + "includesRequiredKnowns": `{{#label}} не содержит {{#knownMisses}}`, + "includesRequiredBoth": `{{#label}} не содержит {{#knownMisses}} и {{#unknownMisses}} других требуемых значений`, + "excludes": `{{#label}} в позиции {{#pos}} содержит исключенное значение`, "excludesSingle": `единственное значение "{{#label}}" содержит исключенное значение`, - "min": `должен содержать не меньше {{#limit}} элементов`, - "max": `должен содержать не больше {{#limit}} элементов`, - "length": `должен содержать {{#limit}} элементов`, - "ordered": `в позиции {{#pos}} не проходит проверку, потому что {{#reason}}`, - "orderedLength": `в позиции {{#pos}} не проходит проверку, потому что массив должен содержать не больше {{#limit}} элементов`, - "sparse": `не должен быть разреженным массивом`, - "unique": `позиция {{#pos}} содержит дублирующееся значение` + "min": `{{#label}} должен содержать не меньше {{#limit}} элементов`, + "max": `{{#label}} должен содержать не больше {{#limit}} элементов`, + "length": `{{#label}} должен содержать {{#limit}} элементов`, + "ordered": `{{#label}} в позиции {{#pos}} не проходит проверку, потому что {{#reason}}`, + "orderedLength": `{{#label}} в позиции {{#pos}} не проходит проверку, потому что массив должен содержать не больше {{#limit}} элементов`, + "sparse": `{{#label}} не должен быть разреженным массивом`, + "unique": `{{#label}} позиция {{#pos}} содержит дублирующееся значение` }, "boolean": { - "base": `должен быть булевым типом` + "base": `{{#label}} должен быть булевым типом` }, "binary": { - "base": `должен быть буфером или строкой`, - "min": `должен быть не меньше {{#limit}} байт`, - "max": `должен быть не больше {{#limit}} байт`, - "length": `должен быть равным {{#limit}} байт` + "base": `{{#label}} должен быть буфером или строкой`, + "min": `{{#label}} должен быть не меньше {{#limit}} байт`, + "max": `{{#label}} должен быть не больше {{#limit}} байт`, + "length": `{{#label}} должен быть равным {{#limit}} байт` }, "date": { - "base": `должен быть числом миллисекунд или валидной датой в строковом формате`, - "min": `должен быть не меньше "{{#limit}}"`, - "max": `должен быть не больше "{{#limit}}"`, - "isoDate": `должен быть валидной датой, соответствующей стандарту ISO 8601`, - "ref": `ссылается на "{{#ref}}", не являющегося датой` + "base": `{{#label}} должен быть числом миллисекунд или валидной датой в строковом формате`, + "min": `{{#label}} должен быть не меньше "{{#limit}}"`, + "max": `{{#label}} должен быть не больше "{{#limit}}"`, + "isoDate": `{{#label}} должен быть валидной датой, соответствующей стандарту ISO 8601`, + "ref": `{{#label}} ссылается на "{{#ref}}", не являющегося датой` }, "function": { - "base": `должен быть функцией` + "base": `{{#label}} должен быть функцией` }, "object": { - "base": `должен быть объектом`, + "base": `{{#label}} должен быть объектом`, "child": `дочерний элемент "{{#label}}" не проходит проверку, потому что {{#reason}}`, - "min": `должен иметь не меньше {{#limit}} дочерних элементов`, - "max": `должен иметь не больше {{#limit}} дочерних элементов`, - "length": `должен иметь {{#limit}} дочерних элементов`, - "allowUnknown": `не разрешен`, - "with": `не найден обязательный сопуствующий ключ "{{#peer}}"`, - "without": `конфликт с запрещённым сопутствующим ключём "{{#peer}}"`, - "missing": `должен содержать не меньше одного из {{#peers}}`, - "xor": `содержит конфликт между взаимоисключающими ключами {{#peers}}`, - "or": `должен содержать не меньше одного из {{#peers}}`, - "and": `содержит {{#present}} без требуемых сопуствующих ключей {{#missing}}`, + "min": `{{#label}} должен иметь не меньше {{#limit}} дочерних элементов`, + "max": `{{#label}} должен иметь не больше {{#limit}} дочерних элементов`, + "length": `{{#label}} должен иметь {{#limit}} дочерних элементов`, + "allowUnknown": `{{#label}} не разрешен`, + "with": `{{#label}} не найден обязательный сопуствующий ключ "{{#peer}}"`, + "without": `{{#label}} конфликт с запрещённым сопутствующим ключём "{{#peer}}"`, + "missing": `{{#label}} должен содержать не меньше одного из {{#peers}}`, + "xor": `{{#label}} содержит конфликт между взаимоисключающими ключами {{#peers}}`, + "or": `{{#label}} должен содержать не меньше одного из {{#peers}}`, + "and": `{{#label}} содержит {{#present}} без требуемых сопуствующих ключей {{#missing}}`, "nand": `"{{#main}}" не должен существовать одновременно с {{#peers}}`, "assert": `"{{#ref}}" не прошел проверку, потому что "{{#ref}}" не {{#message}}`, "rename": { - "multiple": `не может переименовать дочерний элемент "{{#from}}", потому что множественные переименования запрещены и другой ключ уже был переименован в "{{#to}}"`, - "override": `не может переименовать дочерний элемент "{{#from}}", потому что замещение запрещено и цель "{{#to}}" существует` + "multiple": `{{#label}} не может переименовать дочерний элемент "{{#from}}", потому что множественные переименования запрещены и другой ключ уже был переименован в "{{#to}}"`, + "override": `{{#label}} не может переименовать дочерний элемент "{{#from}}", потому что замещение запрещено и цель "{{#to}}" существует` }, "type": `должен быть экземпляром "{{#type}}"` }, "number": { - "base": `должен быть числом`, - "min": `должен быть не меньше {{#limit}}`, - "max": `должен быть не больше {{#limit}}`, - "less": `должен быть меньше {{#limit}}`, - "greater": `должен быть больше {{#limit}}`, - "float": `должен быть числом с плавающей точкой`, - "integer": `должен быть целым числом`, - "negative": `должен быть отрицательным числом`, - "positive": `должен быть положительным числом`, - "precision": `должен иметь не больше {{#limit}} цифр после запятой`, - "ref": `ссылается на "{{#ref}}", не являющегося числом`, - "multiple": `должен быть кратным {{#multiple}}` + "base": `{{#label}} должен быть числом`, + "min": `{{#label}} должен быть не меньше {{#limit}}`, + "max": `{{#label}} должен быть не больше {{#limit}}`, + "less": `{{#label}} должен быть меньше {{#limit}}`, + "greater": `{{#label}} должен быть больше {{#limit}}`, + "float": `{{#label}} должен быть числом с плавающей точкой`, + "integer": `{{#label}} должен быть целым числом`, + "negative": `{{#label}} должен быть отрицательным числом`, + "positive": `{{#label}} должен быть положительным числом`, + "precision": `{{#label}} должен иметь не больше {{#limit}} цифр после запятой`, + "ref": `{{#label}} ссылается на "{{#ref}}", не являющегося числом`, + "multiple": `{{#label}} должен быть кратным {{#multiple}}` }, "string": { - "base": `должен быть строкой`, - "min": `длина должна быть не меньше {{#limit}} символов`, - "max": `длина должна быть не больше {{#limit}} символов`, - "length": `длина должна быть равной {{#limit}}`, - "alphanum": `должен содержать только буквенно-цифровые символы`, - "token": `должен содержать только буквенно-цифровые символы и нижнее подчеркивание`, + "base": `{{#label}} должен быть строкой`, + "min": `{{#label}} длина должна быть не меньше {{#limit}} символов`, + "max": `{{#label}}длина должна быть не больше {{#limit}} символов`, + "length": `{{#label}} длина должна быть равной {{#limit}}`, + "alphanum": `{{#label}} должен содержать только буквенно-цифровые символы`, + "token": `{{#label}} должен содержать только буквенно-цифровые символы и нижнее подчеркивание`, "regex": { - "base": `со значением "{{#value}}" не соответствует требуемому паттерну: {{#pattern}}`, - "name": `со значением "{{#value}}" не соответствует паттерну {{#name}}` + "base": `{{#label}} со значением "{{#value}}" не соответствует требуемому паттерну: {{#pattern}}`, + "name": `{{#label}} со значением "{{#value}}" не соответствует паттерну {{#name}}` }, - "email": `должен быть валидным email-адресом`, - "uri": `должен быть валидным uri`, - "uriCustomScheme": `должен быть валидным uri со схемой, соответствующей паттерну {{#scheme}}`, - "isoDate": `должен соответствовать формату даты ISO 8601`, - "guid": `должен быть валидным GUID`, - "hex": `должен содержать только шестнадцатеричные символы`, - "hostname": `должен быть валидным именем хоста (hostname)`, - "lowercase": `должен содержать только строчные символы`, - "uppercase": `должен содержать только заглавные символы`, - "trim": `не должен содержать пробельные символы в начале или конце строки`, - "creditCard": `должен быть кредитной картой`, - "ref": `ссылается на "{{#ref}}", не являющегося числом`, - "ip": `должен быть валидным ip-адресом с {{#cidr}} CIDR`, - "ipVersion": `должен быть валидным ip-адресом одной из следующих версий {{#version}} с {{#cidr}} CIDR` + "email": `{{#label}} должен быть валидным email-адресом`, + "uri": `{{#label}} должен быть валидным uri`, + "uriCustomScheme": `{{#label}} должен быть валидным uri со схемой, соответствующей паттерну {{#scheme}}`, + "isoDate": `{{#label}} должен соответствовать формату даты ISO 8601`, + "guid": `{{#label}} должен быть валидным GUID`, + "hex": `{{#label}} должен содержать только шестнадцатеричные символы`, + "hostname": `{{#label}} должен быть валидным именем хоста (hostname)`, + "lowercase": `{{#label}} должен содержать только строчные символы`, + "uppercase": `{{#label}} должен содержать только заглавные символы`, + "trim": `{{#label}} не должен содержать пробельные символы в начале или конце строки`, + "creditCard": `{{#label}} должен быть кредитной картой`, + "ref": `{{#label}} ссылается на "{{#ref}}", не являющегося числом`, + "ip": `{{#label}} должен быть валидным ip-адресом с {{#cidr}} CIDR`, + "ipVersion": `{{#label}} должен быть валидным ip-адресом одной из следующих версий {{#version}} с {{#cidr}} CIDR` } }; From 779c51c1e7e24fc1168d14fd2ec065b9b5e025f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 18 Nov 2023 22:46:53 +0300 Subject: [PATCH 08/14] Add registration handler --- src/controllers/user/user.controller.ts | 23 +++++++++++++++++++++++ src/controllers/user/user.dto.ts | 2 +- src/controllers/user/user.service.ts | 2 +- src/controllers/user/user.validation.ts | 6 ++++-- src/pages/api/registration.api.ts | 10 ++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/controllers/user/user.controller.ts create mode 100644 src/pages/api/registration.api.ts diff --git a/src/controllers/user/user.controller.ts b/src/controllers/user/user.controller.ts new file mode 100644 index 0000000..042c5d9 --- /dev/null +++ b/src/controllers/user/user.controller.ts @@ -0,0 +1,23 @@ +import {NextApiRequest, NextApiResponse} from "next"; +import {ApiError} from "../../service/api/error"; +import {UserDto} from "./user.dto"; +import {userService} from "./user.service"; +import {userValidation} from "./user.validation"; + +const userController = { + async registration(req: NextApiRequest, res: NextApiResponse) { + const {error, value} = userValidation.registration(req.body); + + if (error) { + throw ApiError.badRequest(error.message); + } + + const user = await userService.registration(value); + + res.status(200).json(UserDto.toJSON(user)); + } +}; + +export { + userController, +}; diff --git a/src/controllers/user/user.dto.ts b/src/controllers/user/user.dto.ts index eb2648e..de3ca28 100644 --- a/src/controllers/user/user.dto.ts +++ b/src/controllers/user/user.dto.ts @@ -30,7 +30,7 @@ class UserDto { this.isActivated = data.isActivated; } - toJSON(data: Prisma.UserGetPayload): TUserJSON { + static toJSON(data: Prisma.UserGetPayload): TUserJSON { return { id: data.id, name: data.name, diff --git a/src/controllers/user/user.service.ts b/src/controllers/user/user.service.ts index d00cf96..c4a2ee8 100644 --- a/src/controllers/user/user.service.ts +++ b/src/controllers/user/user.service.ts @@ -1,5 +1,5 @@ import bcrypt from "bcrypt"; -import uuid from "uuid"; +import * as uuid from "uuid"; import {ApiError} from "../../service/api/error"; import {prisma} from "../../service/prisma-client"; import {handlePromise, log} from "../../utils/utils"; diff --git a/src/controllers/user/user.validation.ts b/src/controllers/user/user.validation.ts index 5b7cbf6..bd40e1f 100644 --- a/src/controllers/user/user.validation.ts +++ b/src/controllers/user/user.validation.ts @@ -2,7 +2,9 @@ import {ValidationOptions} from "joi"; import joi from "types-joi"; import {validate} from "../../service/api/validation"; -const name = joi.string().alphanum().min(2).required(); +const NAME_REGEXP = /^[a-zA-Zа-яА-Я ]+$/; + +const name = joi.string().regex(NAME_REGEXP).min(2).required(); const password = joi.string().alphanum().min(8).required(); const email = joi.string().email().required(); @@ -12,7 +14,7 @@ const userValidation = { name, password, email, - }), data, options); + }).required(), data, options); } }; diff --git a/src/pages/api/registration.api.ts b/src/pages/api/registration.api.ts new file mode 100644 index 0000000..866063a --- /dev/null +++ b/src/pages/api/registration.api.ts @@ -0,0 +1,10 @@ +import {NextApiRequest, NextApiResponse} from "next"; +import {createRouter} from "next-connect"; +import {userController} from "../../controllers/user/user.controller"; +import {onError} from "../../service/api/middlewares/on-error"; + +const router = createRouter(); + +router.post(userController.registration); + +export default router.handler({onError}); From 5fa8377d10cbc1b63f9377a3d70111a24a249156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sun, 19 Nov 2023 15:11:25 +0300 Subject: [PATCH 09/14] Add user activation handler --- src/const/const.ts | 2 ++ src/controllers/user/user.controller.ts | 14 ++++++++++++++ src/controllers/user/user.service.ts | 4 +++- src/controllers/user/user.validation.ts | 5 +++++ src/pages/api/activation.api.ts | 10 ++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/pages/api/activation.api.ts diff --git a/src/const/const.ts b/src/const/const.ts index 00a7c19..50bf9e4 100644 --- a/src/const/const.ts +++ b/src/const/const.ts @@ -12,6 +12,8 @@ export enum ApiRoute { PUSHER_AUTH = `api/pusher-auth`, TELEGRAM_AUTH = `/api/telegram/auth`, TELEGRAM_SEND_MESSAGE = `/api/telegram/send-message`, + REGISTRATION = `/api/registration`, + USER_ACTIVATION = `/api/activation`, } export const navLinks: TLink[] = [{ diff --git a/src/controllers/user/user.controller.ts b/src/controllers/user/user.controller.ts index 042c5d9..8bffd29 100644 --- a/src/controllers/user/user.controller.ts +++ b/src/controllers/user/user.controller.ts @@ -1,4 +1,5 @@ import {NextApiRequest, NextApiResponse} from "next"; +import {SITE_URL} from "../../../config"; import {ApiError} from "../../service/api/error"; import {UserDto} from "./user.dto"; import {userService} from "./user.service"; @@ -15,6 +16,19 @@ const userController = { const user = await userService.registration(value); res.status(200).json(UserDto.toJSON(user)); + }, + + async activateUser(req: NextApiRequest, res: NextApiResponse) { + const url = `${SITE_URL}${req.url}`; + const {error, value: activationUrl} = userValidation.activationLink(url); + + if (error) { + throw ApiError.badRequest(`Некорректный адрес`, error); + } + + const result = await userService.activateUser(activationUrl); + + res.status(200).json(result); } }; diff --git a/src/controllers/user/user.service.ts b/src/controllers/user/user.service.ts index c4a2ee8..6399fde 100644 --- a/src/controllers/user/user.service.ts +++ b/src/controllers/user/user.service.ts @@ -1,5 +1,7 @@ import bcrypt from "bcrypt"; import * as uuid from "uuid"; +import {SITE_URL} from "../../../config"; +import {ApiRoute} from "../../const/const"; import {ApiError} from "../../service/api/error"; import {prisma} from "../../service/prisma-client"; import {handlePromise, log} from "../../utils/utils"; @@ -11,7 +13,7 @@ type TRegistrationData = { } const createActivationLink = () => { - return uuid.v4(); + return `${SITE_URL}${ApiRoute.USER_ACTIVATION}?id=${uuid.v4()}`; }; const userService = { diff --git a/src/controllers/user/user.validation.ts b/src/controllers/user/user.validation.ts index bd40e1f..0cc4895 100644 --- a/src/controllers/user/user.validation.ts +++ b/src/controllers/user/user.validation.ts @@ -7,6 +7,7 @@ const NAME_REGEXP = /^[a-zA-Zа-яА-Я ]+$/; const name = joi.string().regex(NAME_REGEXP).min(2).required(); const password = joi.string().alphanum().min(8).required(); const email = joi.string().email().required(); +const activationLink = joi.string().uri().required(); const userValidation = { registration(data: any, options?: ValidationOptions) { @@ -15,6 +16,10 @@ const userValidation = { password, email, }).required(), data, options); + }, + + activationLink(url: string, options?: ValidationOptions) { + return validate(activationLink, url, options); } }; diff --git a/src/pages/api/activation.api.ts b/src/pages/api/activation.api.ts new file mode 100644 index 0000000..ca4fa52 --- /dev/null +++ b/src/pages/api/activation.api.ts @@ -0,0 +1,10 @@ +import {NextApiRequest, NextApiResponse} from "next"; +import {createRouter} from "next-connect"; +import {userController} from "../../controllers/user/user.controller"; +import {onError} from "../../service/api/middlewares/on-error"; + +const router = createRouter(); + +router.get(userController.activateUser); + +export default router.handler({onError}); From 8acacbc2ad60fc9698f65edbc10eb4602321eb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 9 Dec 2023 00:01:12 +0300 Subject: [PATCH 10/14] Add custom form, custom input --- public/fonts/inter-bold.woff2 | Bin 0 -> 27788 bytes public/fonts/inter-medium.woff2 | Bin 0 -> 27692 bytes public/fonts/inter-regular.woff | Bin 26120 -> 0 bytes public/fonts/inter-regular.woff2 | Bin 20484 -> 26064 bytes public/fonts/inter-semibold.woff2 | Bin 0 -> 27828 bytes .../custom-form/custom-form.module.css | 10 ++++ src/components/custom-form/custom-form.tsx | 26 ++++++++++ .../custom-input/custom-input.module.css | 45 ++++++++++++++++++ src/components/custom-input/custom-input.tsx | 19 ++++++++ src/components/header/header.module.css | 12 +++++ src/components/header/header.tsx | 8 ++++ src/const/const.ts | 1 + src/pages/registration.page.tsx | 44 +++++++++++++++++ src/styles/fonts.css | 35 +++++++++++--- src/styles/variables.css | 3 +- 15 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 public/fonts/inter-bold.woff2 create mode 100644 public/fonts/inter-medium.woff2 delete mode 100644 public/fonts/inter-regular.woff create mode 100644 public/fonts/inter-semibold.woff2 create mode 100644 src/components/custom-form/custom-form.module.css create mode 100644 src/components/custom-form/custom-form.tsx create mode 100644 src/components/custom-input/custom-input.module.css create mode 100644 src/components/custom-input/custom-input.tsx create mode 100644 src/pages/registration.page.tsx diff --git a/public/fonts/inter-bold.woff2 b/public/fonts/inter-bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..934017df48a63cca73af8af7ded220aaf8bc7fcc GIT binary patch literal 27788 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 diff --git a/public/fonts/inter-regular.woff2 b/public/fonts/inter-regular.woff2 index 14aa3351fc93935299b63011f08ce16aed5cf905..f7a761d9e2dcf053cf1debbaa2e8101c8edc5f0f 100644 GIT binary patch literal 26064 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²EMlsT;%~7?}}tf^it&PGe_E-m1!$O9IQy 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 diff --git a/public/fonts/inter-semibold.woff2 b/public/fonts/inter-semibold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..6664455d87c65b4a7c35ec5beba79435d7032124 GIT binary patch literal 27828 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 & { + className?: string, + children: React.ReactNode, + disabled?: boolean +} + +const CustomForm: React.ForwardRefRenderFunction = ({ + className = ``, + children, + disabled = false, + ...nativeProps +}, ref) => { + return ( +
+
+ {children} +
+
+ ); +}; + +export default React.forwardRef(CustomForm); diff --git a/src/components/custom-input/custom-input.module.css b/src/components/custom-input/custom-input.module.css new file mode 100644 index 0000000..ee78681 --- /dev/null +++ b/src/components/custom-input/custom-input.module.css @@ -0,0 +1,45 @@ +.label { + width: 100%; +} + +.label:hover > .labelText { + opacity: 1; +} + +.labelText { + display: inline-block; + margin-left: 1px; + margin-bottom: 3px; + font-size: 0.9rem; + font-weight: 600; + color: var(--color-text-dark); + opacity: 0.7; + transition: all var(--duration-fast) linear; +} + +.input { + width: 100%; + padding: 7px 15px; + border-radius: var(--border-radius-low); + border: none; + font-size: 1.2rem; + outline: 2px solid var(--color-special); + transition: all var(--duration-fast) linear; +} + +.input::placeholder { + color: var(--color-text-dark); + opacity: 0.4; +} + +.input:hover::placeholder { + opacity: 0.5; +} + +.input:hover { + outline-color: var(--color-special-darken); +} + +.input:focus { + outline-color: var(--color-special-darkest); +} \ No newline at end of file diff --git a/src/components/custom-input/custom-input.tsx b/src/components/custom-input/custom-input.tsx new file mode 100644 index 0000000..1c6cda3 --- /dev/null +++ b/src/components/custom-input/custom-input.tsx @@ -0,0 +1,19 @@ +import {clsx} from "clsx"; +import React, {InputHTMLAttributes} from "react"; +import customInputStyle from "./custom-input.module.css"; + +type TProps = InputHTMLAttributes & { + className?: string, + label?: string, +} + +const CustomInput: React.FC = ({className = ``, label, ...props}) => { + return ( + + ); +}; + +export default CustomInput; diff --git a/src/components/header/header.module.css b/src/components/header/header.module.css index 56c5baf..994b012 100644 --- a/src/components/header/header.module.css +++ b/src/components/header/header.module.css @@ -1,3 +1,15 @@ .header { + display: flex; + flex-direction: row; background: var(--gradient-gray); +} + +.userNav { + margin: auto 0 auto auto; + height: 100%; +} + +.userNavRegLink { + display: inline-block; + color: var(--color-special); } \ No newline at end of file diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index 7a2fc49..ae7b21e 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,5 +1,7 @@ import {clsx} from "clsx"; +import Link from "next/link"; import React from "react"; +import {PageRoute} from "../../const/const"; import {TLink} from "../../const/const.types"; import SiteNav from "../site-nav/site-nav"; import headerStyle from "./header.module.css"; @@ -19,6 +21,12 @@ const Header: React.FC = ({className, content}) => { return (
+ +
+ + Регистрация + +
); }; diff --git a/src/const/const.ts b/src/const/const.ts index 50bf9e4..ede6a54 100644 --- a/src/const/const.ts +++ b/src/const/const.ts @@ -6,6 +6,7 @@ export enum PageRoute { TELEGRAM_FEATURES = `/telegram/features`, BLOG = `/blog`, POST = `/post`, + REGISTRATION = `/registration`, } export enum ApiRoute { diff --git a/src/pages/registration.page.tsx b/src/pages/registration.page.tsx new file mode 100644 index 0000000..114d2ce --- /dev/null +++ b/src/pages/registration.page.tsx @@ -0,0 +1,44 @@ +import {clsx} from "clsx"; +import {GetStaticProps} from "next"; +import React from "react"; +import {SITE_URL} from "../../config"; +import Layout, {TLayoutContent} from "../components/layout/layout"; +import {PageRoute} from "../const/const"; +import {createContent} from "../content/base-content"; +import CustomForm from "../components/custom-form/custom-form"; +import CustomInput from "../components/custom-input/custom-input"; + +const TITLE = `Главная`; +const DESCRIPTION = `Главная страница`; +const CANONICAL = `${SITE_URL}${PageRoute.MAIN}`; + +type TProps = { + content: { + layout: TLayoutContent, + }, +} + +const RegistrationPage: React.FC = ({content}) => { + const {layout} = content; + + return ( + +
+

Страница регистрации

+ + + +
+
+ ); +}; + +export const getStaticProps: GetStaticProps = async () => { + return { + props: { + content: createContent(true), + } + }; +}; + +export default RegistrationPage; diff --git a/src/styles/fonts.css b/src/styles/fonts.css index 571a048..2f4de8f 100644 --- a/src/styles/fonts.css +++ b/src/styles/fonts.css @@ -1,8 +1,31 @@ @font-face { - font-family: "Inter"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("/fonts/inter-regular.woff2") format("woff2"), - url("/fonts/inter-regular.woff") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("/fonts/inter-regular.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("/fonts/inter-medium.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url("/fonts/inter-semibold.woff2") format("woff2"); +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("/fonts/inter-bold.woff2") format("woff2"); } \ No newline at end of file diff --git a/src/styles/variables.css b/src/styles/variables.css index beb9e3d..eb1fead 100644 --- a/src/styles/variables.css +++ b/src/styles/variables.css @@ -17,6 +17,7 @@ --box-shadow-dark-blue: 0px 0px 10px 0px rgba(11, 45, 71, 0.35); /* border-radius */ + --border-radius-low: 5px; --border-radius-middle: 10px; /* gradients */ @@ -35,7 +36,7 @@ --font-main: "Inter", Arial, Helvetica, sans-serif; /* font size */ - --font-size-main: 16px; + --font-size-main: 14px; --font-size-h1: 2rem; --font-size-h2: 1.5rem; --font-size-h3: 1.17rem; From efa7bbe858f57d96f2b25b2ce60d5fb2bfe8a99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 9 Dec 2023 18:40:53 +0300 Subject: [PATCH 11/14] Add registration form --- prisma/schema.prisma | 1 + .../custom-button/custom-button.module.css | 24 +++++++++++++++++++ .../custom-button/custom-button.tsx | 18 ++++++++++++++ .../custom-input/custom-input.module.css | 7 +++--- src/components/custom-input/custom-input.tsx | 2 +- src/pages/registration.page.tsx | 17 +++++++++---- src/styles/globals.css | 4 ++++ src/styles/registration-page.module.css | 7 ++++++ src/styles/variables.css | 5 ++-- 9 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 src/components/custom-button/custom-button.module.css create mode 100644 src/components/custom-button/custom-button.tsx create mode 100644 src/styles/registration-page.module.css diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6645de6..899e5bb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -12,6 +12,7 @@ datasource db { model User { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String + username String @unique password String email String @unique role Role @default(USER) diff --git a/src/components/custom-button/custom-button.module.css b/src/components/custom-button/custom-button.module.css new file mode 100644 index 0000000..32a8b87 --- /dev/null +++ b/src/components/custom-button/custom-button.module.css @@ -0,0 +1,24 @@ +.button { + max-width: max-content; + padding: 5px 15px; + display: inline-block; + background-color: var(--color-special); + border: 2px solid var(--color-bg-gray-darken); + border-radius: var(--border-radius-low); + font-size: 1.2rem; + cursor: pointer; + transition: background-color, transform var(--duration-fast); + color: var(--color-text-dark); +} + +.button:hover { + background-color: var(--color-special-darken); +} + +.button:focus { + outline: 2px solid var(--color-special-darkest); +} + +.button:active { + transform: scale(0.98); +} \ No newline at end of file diff --git a/src/components/custom-button/custom-button.tsx b/src/components/custom-button/custom-button.tsx new file mode 100644 index 0000000..2671948 --- /dev/null +++ b/src/components/custom-button/custom-button.tsx @@ -0,0 +1,18 @@ +import {clsx} from "clsx"; +import React, {ButtonHTMLAttributes} from "react"; +import customButtonStyle from "./custom-button.module.css"; + +type TProps = ButtonHTMLAttributes & { + className?: string, + children?: React.ReactNode, +} + +const CustomButton: React.FC = ({className = ``, children, ...props}) => { + return ( + + ); +}; + +export default CustomButton; diff --git a/src/components/custom-input/custom-input.module.css b/src/components/custom-input/custom-input.module.css index ee78681..1c7d9ab 100644 --- a/src/components/custom-input/custom-input.module.css +++ b/src/components/custom-input/custom-input.module.css @@ -2,7 +2,8 @@ width: 100%; } -.label:hover > .labelText { +.label:hover > .labelText, +.label:focus-within > .labelText { opacity: 1; } @@ -10,7 +11,7 @@ display: inline-block; margin-left: 1px; margin-bottom: 3px; - font-size: 0.9rem; + font-size: 1rem; font-weight: 600; color: var(--color-text-dark); opacity: 0.7; @@ -24,7 +25,6 @@ border: none; font-size: 1.2rem; outline: 2px solid var(--color-special); - transition: all var(--duration-fast) linear; } .input::placeholder { @@ -41,5 +41,6 @@ } .input:focus { + outline-width: 3px; outline-color: var(--color-special-darkest); } \ No newline at end of file diff --git a/src/components/custom-input/custom-input.tsx b/src/components/custom-input/custom-input.tsx index 1c6cda3..dd762ea 100644 --- a/src/components/custom-input/custom-input.tsx +++ b/src/components/custom-input/custom-input.tsx @@ -10,7 +10,7 @@ type TProps = InputHTMLAttributes & { const CustomInput: React.FC = ({className = ``, label, ...props}) => { return ( ); diff --git a/src/pages/registration.page.tsx b/src/pages/registration.page.tsx index 114d2ce..aa54618 100644 --- a/src/pages/registration.page.tsx +++ b/src/pages/registration.page.tsx @@ -2,11 +2,13 @@ import {clsx} from "clsx"; import {GetStaticProps} from "next"; import React from "react"; import {SITE_URL} from "../../config"; +import CustomButton from "../components/custom-button/custom-button"; +import CustomForm from "../components/custom-form/custom-form"; +import CustomInput from "../components/custom-input/custom-input"; import Layout, {TLayoutContent} from "../components/layout/layout"; import {PageRoute} from "../const/const"; import {createContent} from "../content/base-content"; -import CustomForm from "../components/custom-form/custom-form"; -import CustomInput from "../components/custom-input/custom-input"; +import registrationPageStyle from "../styles/registration-page.module.css"; const TITLE = `Главная`; const DESCRIPTION = `Главная страница`; @@ -24,9 +26,14 @@ const RegistrationPage: React.FC = ({content}) => { return (
-

Страница регистрации

- - +

Страница регистрации

+ + + + + + + Регистрация
diff --git a/src/styles/globals.css b/src/styles/globals.css index 34330f3..3643661 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -45,6 +45,10 @@ a { margin-bottom: var(--margin-block-middle); } +.mrgb-low { + margin-bottom: var(--margin-block-low); +} + h1 { font-size: var(--font-size-h1); } diff --git a/src/styles/registration-page.module.css b/src/styles/registration-page.module.css new file mode 100644 index 0000000..95ab178 --- /dev/null +++ b/src/styles/registration-page.module.css @@ -0,0 +1,7 @@ +.registerForm > fieldset { + padding: 20px; + border: 2px solid var(--color-special); + border-radius: var(--border-radius-low); + display: grid; + grid-gap: 10px; +} \ No newline at end of file diff --git a/src/styles/variables.css b/src/styles/variables.css index eb1fead..8f8b675 100644 --- a/src/styles/variables.css +++ b/src/styles/variables.css @@ -6,8 +6,8 @@ --color-bg-gray-darken: rgba(166, 154, 154, 0.81); --color-bg-main: #f3e0e0; --color-special: #ff9927; - --color-special-darken: #ef9025; - --color-special-darkest: #e58821; + --color-special-darken: #fd891b; + --color-special-darkest: #f18009; --color-text-light: #ffffff; --color-text-dark: #000000; --color-text-gray-dark: #3b3535; @@ -28,6 +28,7 @@ --padding-container-tablet: 0 30px; --padding-container-mobile: 0 20px; --margin-block-middle: 40px; + --margin-block-low: 20px; /* page size */ --page-size-max: 1920px; From 986cfda31d75041a91fcb5e24859e4c2073b18e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 16 Dec 2023 19:29:24 +0300 Subject: [PATCH 12/14] Add registration api --- src/const/const.ts | 4 +- src/const/const.types.ts | 2 + src/controllers/user/user.api.ts | 25 ++++++++++ src/controllers/user/user.controller.ts | 2 +- src/controllers/user/user.dto.ts | 14 +----- src/controllers/user/user.service.ts | 18 ++----- src/controllers/user/user.types.ts | 17 +++++++ src/controllers/user/user.utils.ts | 7 +++ src/controllers/user/user.validation.ts | 3 ++ src/pages/api/{ => user}/registration.api.ts | 4 +- src/pages/registration.page.tsx | 50 ++++++++++++++++---- src/service/api/api.ts | 11 ++++- 12 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 src/controllers/user/user.api.ts create mode 100644 src/controllers/user/user.types.ts create mode 100644 src/controllers/user/user.utils.ts rename src/pages/api/{ => user}/registration.api.ts (63%) diff --git a/src/const/const.ts b/src/const/const.ts index ede6a54..53f85d0 100644 --- a/src/const/const.ts +++ b/src/const/const.ts @@ -10,11 +10,13 @@ export enum PageRoute { } export enum ApiRoute { + USER = `api/user`, + USER_ACTIVATION = `/api/user/activation`, + USER_REGISTRATION = `/api/user/registration`, PUSHER_AUTH = `api/pusher-auth`, TELEGRAM_AUTH = `/api/telegram/auth`, TELEGRAM_SEND_MESSAGE = `/api/telegram/send-message`, REGISTRATION = `/api/registration`, - USER_ACTIVATION = `/api/activation`, } export const navLinks: TLink[] = [{ diff --git a/src/const/const.types.ts b/src/const/const.types.ts index 7734797..62e0a73 100644 --- a/src/const/const.types.ts +++ b/src/const/const.types.ts @@ -2,3 +2,5 @@ export type TLink = { name: string, url: string, } + +export type RC = [TError, null] | [null, TResult]; diff --git a/src/controllers/user/user.api.ts b/src/controllers/user/user.api.ts new file mode 100644 index 0000000..c0d1ef9 --- /dev/null +++ b/src/controllers/user/user.api.ts @@ -0,0 +1,25 @@ +import {ApiRoute} from "../../const/const"; +import {RC} from "../../const/const.types"; +import {Api} from "../../service/api/api"; +import {ApiError} from "../../service/api/error"; +import {TApiSuccessData} from "../../service/api/types"; +import {handlePromise} from "../../utils/utils"; +import {TRegistrationData, TUserJSON} from "./user.types"; + +const api = new Api(); + +const userApi = { + async registration(data: TRegistrationData): Promise> { + const [error, result] = await handlePromise, ApiError>(api.post(ApiRoute.USER_REGISTRATION, data)); + + if (error) { + return [error, null]; + } + + return [null, result.data]; + } +}; + +export { + userApi, +}; diff --git a/src/controllers/user/user.controller.ts b/src/controllers/user/user.controller.ts index 8bffd29..2b4da34 100644 --- a/src/controllers/user/user.controller.ts +++ b/src/controllers/user/user.controller.ts @@ -7,7 +7,7 @@ import {userValidation} from "./user.validation"; const userController = { async registration(req: NextApiRequest, res: NextApiResponse) { - const {error, value} = userValidation.registration(req.body); + const {error, value} = userValidation.registration(JSON.parse(req.body)); if (error) { throw ApiError.badRequest(error.message); diff --git a/src/controllers/user/user.dto.ts b/src/controllers/user/user.dto.ts index de3ca28..adf0583 100644 --- a/src/controllers/user/user.dto.ts +++ b/src/controllers/user/user.dto.ts @@ -1,13 +1,5 @@ import {Prisma, Role} from "@prisma/client"; - -type TUserJSON = { - id: string, - name: string, - email: string, - role: Role, - createdAt: string, - updatedAt: string, -} +import {TUserJSON} from "./user.types"; class UserDto { id: string; @@ -42,10 +34,6 @@ class UserDto { } } -export type { - TUserJSON, -}; - export { UserDto, }; diff --git a/src/controllers/user/user.service.ts b/src/controllers/user/user.service.ts index 6399fde..a2ac872 100644 --- a/src/controllers/user/user.service.ts +++ b/src/controllers/user/user.service.ts @@ -1,20 +1,9 @@ import bcrypt from "bcrypt"; -import * as uuid from "uuid"; -import {SITE_URL} from "../../../config"; -import {ApiRoute} from "../../const/const"; import {ApiError} from "../../service/api/error"; import {prisma} from "../../service/prisma-client"; import {handlePromise, log} from "../../utils/utils"; - -type TRegistrationData = { - name: string, - email: string, - password: string, -} - -const createActivationLink = () => { - return `${SITE_URL}${ApiRoute.USER_ACTIVATION}?id=${uuid.v4()}`; -}; +import {TRegistrationData} from "./user.types"; +import {createActivationLink} from "./user.utils"; const userService = { async registration(data: TRegistrationData) { @@ -33,13 +22,14 @@ const userService = { throw ApiError.badRequest(`Пользователь с таким емейлом уже существует`); } - const {name, email, password} = data; + const {name, email, password, username} = data; const hashingPassword = await bcrypt.hash(password, 5); const activationLink = createActivationLink(); const [creatingError, newUser] = await handlePromise(prisma.user.create({ data: { name, + username, email: email.toLowerCase(), password: hashingPassword, isActivated: false, diff --git a/src/controllers/user/user.types.ts b/src/controllers/user/user.types.ts new file mode 100644 index 0000000..8c64da0 --- /dev/null +++ b/src/controllers/user/user.types.ts @@ -0,0 +1,17 @@ +import {Role} from "@prisma/client"; + +export type TRegistrationData = { + name: string, + email: string, + password: string, + username: string, +} + +export type TUserJSON = { + id: string, + name: string, + email: string, + role: Role, + createdAt: string, + updatedAt: string, +} diff --git a/src/controllers/user/user.utils.ts b/src/controllers/user/user.utils.ts new file mode 100644 index 0000000..f4f2ac8 --- /dev/null +++ b/src/controllers/user/user.utils.ts @@ -0,0 +1,7 @@ +import * as uuid from "uuid"; +import {SITE_URL} from "../../../config"; +import {ApiRoute} from "../../const/const"; + +export const createActivationLink = () => { + return `${SITE_URL}${ApiRoute.USER_ACTIVATION}?id=${uuid.v4()}`; +}; diff --git a/src/controllers/user/user.validation.ts b/src/controllers/user/user.validation.ts index 0cc4895..4934994 100644 --- a/src/controllers/user/user.validation.ts +++ b/src/controllers/user/user.validation.ts @@ -3,10 +3,12 @@ import joi from "types-joi"; import {validate} from "../../service/api/validation"; const NAME_REGEXP = /^[a-zA-Zа-яА-Я ]+$/; +const USERNAME_REGEXP = /^[a-zA-Z0-9-]+$/; const name = joi.string().regex(NAME_REGEXP).min(2).required(); const password = joi.string().alphanum().min(8).required(); const email = joi.string().email().required(); +const username = joi.string().min(3).max(24).regex(USERNAME_REGEXP).required(); const activationLink = joi.string().uri().required(); const userValidation = { @@ -15,6 +17,7 @@ const userValidation = { name, password, email, + username, }).required(), data, options); }, diff --git a/src/pages/api/registration.api.ts b/src/pages/api/user/registration.api.ts similarity index 63% rename from src/pages/api/registration.api.ts rename to src/pages/api/user/registration.api.ts index 866063a..702cb11 100644 --- a/src/pages/api/registration.api.ts +++ b/src/pages/api/user/registration.api.ts @@ -1,7 +1,7 @@ import {NextApiRequest, NextApiResponse} from "next"; import {createRouter} from "next-connect"; -import {userController} from "../../controllers/user/user.controller"; -import {onError} from "../../service/api/middlewares/on-error"; +import {userController} from "../../../controllers/user/user.controller"; +import {onError} from "../../../service/api/middlewares/on-error"; const router = createRouter(); diff --git a/src/pages/registration.page.tsx b/src/pages/registration.page.tsx index aa54618..baf34bb 100644 --- a/src/pages/registration.page.tsx +++ b/src/pages/registration.page.tsx @@ -1,6 +1,6 @@ import {clsx} from "clsx"; import {GetStaticProps} from "next"; -import React from "react"; +import React, {useCallback} from "react"; import {SITE_URL} from "../../config"; import CustomButton from "../components/custom-button/custom-button"; import CustomForm from "../components/custom-form/custom-form"; @@ -8,6 +8,8 @@ import CustomInput from "../components/custom-input/custom-input"; import Layout, {TLayoutContent} from "../components/layout/layout"; import {PageRoute} from "../const/const"; import {createContent} from "../content/base-content"; +import {userApi} from "../controllers/user/user.api"; +import {TRegistrationData} from "../controllers/user/user.types"; import registrationPageStyle from "../styles/registration-page.module.css"; const TITLE = `Главная`; @@ -20,6 +22,43 @@ type TProps = { }, } +const RegistrationForm: React.FC = () => { + const onSubmit = useCallback(async (evt: React.FormEvent) => { + evt.preventDefault(); + const form = evt.target as HTMLFormElement; + + const formData: TRegistrationData = { + name: (form.elements.namedItem(`name`) as HTMLInputElement)?.value || ``, + email: form.email.value, + password: form.password.value, + username: form.username.value, + }; + + const [error, user] = await userApi.registration(formData); + + let resultMessage; + if (error) { + resultMessage = error.message; + } else { + resultMessage = JSON.stringify(user, null, 2); + } + + // eslint-disable-next-line no-alert + alert(resultMessage); + }, []); + + return ( + + + + + + + Регистрация + + ); +}; + const RegistrationPage: React.FC = ({content}) => { const {layout} = content; @@ -27,14 +66,7 @@ const RegistrationPage: React.FC = ({content}) => {

Страница регистрации

- - - - - - - Регистрация - +
); diff --git a/src/service/api/api.ts b/src/service/api/api.ts index c5452af..5b47cb5 100644 --- a/src/service/api/api.ts +++ b/src/service/api/api.ts @@ -6,7 +6,7 @@ import {TApiErrorData, TApiSuccessData} from "./types"; enum ApiMethod { GET = `GET`, - // POST = `POST`, + POST = `POST`, // PATCH = `PATCH`, // PUT = `PUT`, // DELETE = `DELETE`, @@ -90,6 +90,15 @@ class Api { parser: ApiParser.JSON, }); } + + async post, TResult = unknown>(url: string | null = null, body?: TBody, options?: Omit, `body` | `method` | `parser`>) { + return await request(`${this._baseUrl}${url || ``}`, { + ...options, + ...(body ? {body: JSON.stringify(body)} : {}), + method: ApiMethod.POST, + parser: ApiParser.JSON, + }); + } } export { From 6551b4bb7f266316f9379710e05a4d65977b01fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Sat, 16 Dec 2023 19:42:10 +0300 Subject: [PATCH 13/14] Fix username uniqueness --- prisma/schema.prisma | 4 ++-- src/controllers/user/user.service.ts | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 899e5bb..a5b8d86 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -6,13 +6,13 @@ generator client { datasource db { provider = "postgresql" url = env("POSTGRES_CONNECTION_URL") - extensions = [pgcrypto] + extensions = [pgcrypto, citext] } model User { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String - username String @unique + username String @unique @db.Citext password String email String @unique role Role @default(USER) diff --git a/src/controllers/user/user.service.ts b/src/controllers/user/user.service.ts index a2ac872..62eda62 100644 --- a/src/controllers/user/user.service.ts +++ b/src/controllers/user/user.service.ts @@ -9,7 +9,10 @@ const userService = { async registration(data: TRegistrationData) { const [findingError, user] = await handlePromise(prisma.user.findFirst({ where: { - email: data.email.toLowerCase(), + OR: [ + {email: data.email.toLowerCase()}, + {username: data.username}, + ], } })); @@ -19,7 +22,10 @@ const userService = { } if (user) { - throw ApiError.badRequest(`Пользователь с таким емейлом уже существует`); + const errorMessage = user.email === data.email.toLowerCase() + ? `Пользователь с таким емейлом уже существует` + : `Пользователь с таким ником уже существует`; + throw ApiError.badRequest(errorMessage); } const {name, email, password, username} = data; @@ -38,7 +44,7 @@ const userService = { })); if (creatingError) { - log(`User Service Registration`, findingError); + log(`User Service Registration`, creatingError); throw ApiError.internalServerError(); } From caf8b61dc40e0600d9d20d1ec1825aac5410fe11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Mon, 25 Dec 2023 00:26:28 +0300 Subject: [PATCH 14/14] Add mail service --- config.js | 18 +++++++++++++----- package-lock.json | 19 +++++++++++++++++++ package.json | 2 ++ src/service/mail.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 src/service/mail.ts diff --git a/config.js b/config.js index b4b4214..91d6964 100644 --- a/config.js +++ b/config.js @@ -1,12 +1,16 @@ // Private -const TELEGRAM_API_ID = Number(process.env.TELEGRAM_API_ID); -const TELEGRAM_API_HASH = String(process.env.TELEGRAM_API_HASH || ``); +const JWT_ACCESS_KEY = String(process.env.JWT_ACCESS_KEY || ``); +const POSTGRES_CONNECTION_URL = String(process.env.POSTGRES_CONNECTION_URL || ``); const PUSHER_APP_ID = String(process.env.PUSHER_APP_ID); -const PUSHER_SECRET = String(process.env.PUSHER_SECRET || ``); const PUSHER_CLUSTER = String(process.env.NEXT_PUBLIC_PUSHER_CLUSTER || ``); +const PUSHER_SECRET = String(process.env.PUSHER_SECRET || ``); +const SMTP_HOST = String(process.env.SMTP_HOST || ``); +const SMTP_LOGIN = String(process.env.SMTP_LOGIN || ``); +const SMTP_PASSWORD = String(process.env.SMTP_PASSWORD || ``); +const SMTP_PORT = Number(process.env.SMTP_PORT || ``); +const TELEGRAM_API_HASH = String(process.env.TELEGRAM_API_HASH || ``); +const TELEGRAM_API_ID = Number(process.env.TELEGRAM_API_ID); const TELEGRAM_AUTH_STRING = String(process.env.TELEGRAM_AUTH_STRING); -const POSTGRES_CONNECTION_URL = String(process.env.POSTGRES_CONNECTION_URL || ``); -const JWT_ACCESS_KEY = String(process.env.JWT_ACCESS_KEY || ``); // Public const SITE_URL = String(process.env.NEXT_PUBLIC_SITE_URL || ``); @@ -23,4 +27,8 @@ module.exports = { TELEGRAM_API_HASH, TELEGRAM_API_ID, TELEGRAM_AUTH_STRING, + SMTP_HOST, + SMTP_PORT, + SMTP_LOGIN, + SMTP_PASSWORD, }; diff --git a/package-lock.json b/package-lock.json index a438edd..89736ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "jsonwebtoken": "9.0.2", "next": "14.0.1", "next-connect": "1.0.0", + "nodemailer": "6.9.7", "pusher": "5.1.3", "pusher-js": "8.3.0", "react": "18.2.0", @@ -32,6 +33,7 @@ "@types/bcrypt": "5.0.2", "@types/joi": "17.2.3", "@types/jsonwebtoken": "9.0.5", + "@types/nodemailer": "6.4.14", "@types/react": "18.2.34", "@types/react-dom": "18.2.14", "@types/uuid": "9.0.7", @@ -1728,6 +1730,15 @@ "form-data": "^4.0.0" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.14", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.14.tgz", + "integrity": "sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", @@ -7541,6 +7552,14 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/nodemailer": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", + "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", diff --git a/package.json b/package.json index fe7436e..7f696dc 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "jsonwebtoken": "9.0.2", "next": "14.0.1", "next-connect": "1.0.0", + "nodemailer": "6.9.7", "pusher": "5.1.3", "pusher-js": "8.3.0", "react": "18.2.0", @@ -38,6 +39,7 @@ "@types/bcrypt": "5.0.2", "@types/joi": "17.2.3", "@types/jsonwebtoken": "9.0.5", + "@types/nodemailer": "6.4.14", "@types/react": "18.2.34", "@types/react-dom": "18.2.14", "@types/uuid": "9.0.7", diff --git a/src/service/mail.ts b/src/service/mail.ts new file mode 100644 index 0000000..2fc4a35 --- /dev/null +++ b/src/service/mail.ts @@ -0,0 +1,42 @@ +import nodemailer from "nodemailer"; +import {SMTP_HOST, SMTP_PORT, SMTP_LOGIN, SMTP_PASSWORD} from "../../config"; + +const FROM = `Pet Project <${SMTP_LOGIN}>`; + +type TSendMailOptions = { + to: string, + title: string, + text?: string, + html?: string, +} + +const createTransport = () => { + return nodemailer.createTransport({ + host: SMTP_HOST, + port: SMTP_PORT, + secure: true, + auth: { + user: SMTP_LOGIN, + pass: SMTP_PASSWORD, + } + }); +}; + +const mailService = { + async sendMail({to, html, title, text}: TSendMailOptions) { + const transport = createTransport(); + + return await transport.sendMail({ + from: FROM, + to, + subject: title, + text: text || ``, + html: html || ``, + attachDataUrls: true, + }); + } +}; + +export { + mailService, +};