From 9aef927eebdad110dc9c914c3c69d4adb4ae9fc8 Mon Sep 17 00:00:00 2001 From: anibilag Date: Thu, 6 Mar 2025 16:24:41 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=B2,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=20=D0=BF=D0=BE=20=D1=81=D1=82=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=D1=8F=D0=BC=20=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=B2?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 570 +++++++++++++++++- package.json | 4 +- .../migration.sql | 3 + .../20250304094438_add_user_bio/migration.sql | 2 + .../migration.sql | 2 + prisma/schema.prisma | 2 + src/routes/articles/controllers/list.ts | 1 + src/routes/articles/controllers/search.ts | 34 +- src/routes/authors/controllers/authors.ts | 14 + src/routes/authors/index.ts | 9 + src/routes/users/controllers/users.ts | 68 ++- src/routes/users/index.ts | 2 +- src/server.ts | 2 + src/services/userService.ts | 38 +- src/types/auth.ts | 10 + 15 files changed, 678 insertions(+), 83 deletions(-) create mode 100644 prisma/migrations/20250226111843_add_is_active/migration.sql create mode 100644 prisma/migrations/20250304094438_add_user_bio/migration.sql create mode 100644 prisma/migrations/20250304095229_add_user_order/migration.sql create mode 100644 src/routes/authors/controllers/authors.ts create mode 100644 src/routes/authors/index.ts diff --git a/package-lock.json b/package-lock.json index 268716e..5ec880b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.734.0", "@aws-sdk/s3-request-presigner": "^3.734.0", - "@prisma/client": "^6.3.1", + "@prisma/client": "^6.4.1", "axios": "^1.7.9", "bcryptjs": "^2.4.3", "cors": "^2.8.5", @@ -37,7 +37,7 @@ "@types/multer-s3": "^3.0.3", "@types/node": "^22.10.7", "@types/winston": "^2.4.4", - "prisma": "^6.3.1", + "prisma": "^6.4.1", "ts-node": "^10.9.2", "typescript": "^5.7.3" } @@ -990,6 +990,431 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -1380,9 +1805,9 @@ } }, "node_modules/@prisma/client": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.3.1.tgz", - "integrity": "sha512-ARAJaPs+eBkemdky/XU3cvGRl+mIPHCN2lCXsl5Vlb0E2gV+R6IN7aCI8CisRGszEZondwIsW9Iz8EJkTdykyA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.4.1.tgz", + "integrity": "sha512-A7Mwx44+GVZVexT5e2GF/WcKkEkNNKbgr059xpr5mn+oUm2ZW1svhe+0TRNBwCdzhfIZ+q23jEgsNPvKD9u+6g==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -1402,53 +1827,53 @@ } }, "node_modules/@prisma/debug": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.3.1.tgz", - "integrity": "sha512-RrEBkd+HLZx+ydfmYT0jUj7wjLiS95wfTOSQ+8FQbvb6vHh5AeKfEPt/XUQ5+Buljj8hltEfOslEW57/wQIVeA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.4.1.tgz", + "integrity": "sha512-Q9xk6yjEGIThjSD8zZegxd5tBRNHYd13GOIG0nLsanbTXATiPXCLyvlYEfvbR2ft6dlRsziQXfQGxAgv7zcMUA==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.3.1.tgz", - "integrity": "sha512-sXdqEVLyGAJ5/iUoG/Ea5AdHMN71m6PzMBWRQnLmhhOejzqAaEr8rUd623ql6OJpED4s/U4vIn4dg1qkF7vGag==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.4.1.tgz", + "integrity": "sha512-KldENzMHtKYwsOSLThghOIdXOBEsfDuGSrxAZjMnimBiDKd3AE4JQ+Kv+gBD/x77WoV9xIPf25GXMWffXZ17BA==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.3.1", - "@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", - "@prisma/fetch-engine": "6.3.1", - "@prisma/get-platform": "6.3.1" + "@prisma/debug": "6.4.1", + "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "@prisma/fetch-engine": "6.4.1", + "@prisma/get-platform": "6.4.1" } }, "node_modules/@prisma/engines-version": { - "version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0.tgz", - "integrity": "sha512-R/ZcMuaWZT2UBmgX3Ko6PAV3f8//ZzsjRIG1eKqp3f2rqEqVtCv+mtzuH2rBPUC9ujJ5kCb9wwpxeyCkLcHVyA==", + "version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d.tgz", + "integrity": "sha512-Xq54qw55vaCGrGgIJqyDwOq0TtjZPJEWsbQAHugk99hpDf2jcEeQhUcF+yzEsSqegBaDNLA4IC8Nn34sXmkiTQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.3.1.tgz", - "integrity": "sha512-HOf/0umOgt+/S2xtZze+FHKoxpVg4YpVxROr6g2YG09VsI3Ipyb+rGvD6QGbCqkq5NTWAAZoOGNL+oy7t+IhaQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.4.1.tgz", + "integrity": "sha512-uZ5hVeTmDspx7KcaRCNoXmcReOD+84nwlO2oFvQPRQh9xiFYnnUKDz7l9bLxp8t4+25CsaNlgrgilXKSQwrIGQ==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.3.1", - "@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", - "@prisma/get-platform": "6.3.1" + "@prisma/debug": "6.4.1", + "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "@prisma/get-platform": "6.4.1" } }, "node_modules/@prisma/get-platform": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.3.1.tgz", - "integrity": "sha512-AYLq6Hk9xG73JdLWJ3Ip9Wg/vlP7xPvftGBalsPzKDOHr/ImhwJ09eS8xC2vNT12DlzGxhfk8BkL0ve2OriNhQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.4.1.tgz", + "integrity": "sha512-gXqZaDI5scDkBF8oza7fOD3Q3QMD0e0rBynlzDDZdTWbWmzjuW58PRZtj+jkvKje2+ZigCWkH8SsWZAsH6q1Yw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.3.1" + "@prisma/debug": "6.4.1" } }, "node_modules/@smithy/abort-controller": { @@ -2940,6 +3365,85 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/esbuild-register/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild-register/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3719,14 +4223,16 @@ "license": "MIT" }, "node_modules/prisma": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.3.1.tgz", - "integrity": "sha512-JKCZWvBC3enxk51tY4TWzS4b5iRt4sSU1uHn2I183giZTvonXaQonzVtjLzpOHE7qu9MxY510kAtFGJwryKe3Q==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.4.1.tgz", + "integrity": "sha512-q2uJkgXnua/jj66mk6P9bX/zgYJFI/jn4Yp0aS6SPRrjH/n6VyOV7RDe1vHD0DX8Aanx4MvgmUPPoYnR6MJnPg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "6.3.1" + "@prisma/engines": "6.4.1", + "esbuild": ">=0.12 <1", + "esbuild-register": "3.6.0" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index 54ffc50..3bec747 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.734.0", "@aws-sdk/s3-request-presigner": "^3.734.0", - "@prisma/client": "^6.3.1", + "@prisma/client": "^6.4.1", "axios": "^1.7.9", "bcryptjs": "^2.4.3", "cors": "^2.8.5", @@ -40,7 +40,7 @@ "@types/multer-s3": "^3.0.3", "@types/node": "^22.10.7", "@types/winston": "^2.4.4", - "prisma": "^6.3.1", + "prisma": "^6.4.1", "ts-node": "^10.9.2", "typescript": "^5.7.3" } diff --git a/prisma/migrations/20250226111843_add_is_active/migration.sql b/prisma/migrations/20250226111843_add_is_active/migration.sql new file mode 100644 index 0000000..902e7f9 --- /dev/null +++ b/prisma/migrations/20250226111843_add_is_active/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Article" ADD COLUMN "importId" INTEGER NOT NULL DEFAULT 0, +ADD COLUMN "isActive" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20250304094438_add_user_bio/migration.sql b/prisma/migrations/20250304094438_add_user_bio/migration.sql new file mode 100644 index 0000000..c6b3456 --- /dev/null +++ b/prisma/migrations/20250304094438_add_user_bio/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "bio" TEXT; diff --git a/prisma/migrations/20250304095229_add_user_order/migration.sql b/prisma/migrations/20250304095229_add_user_order/migration.sql new file mode 100644 index 0000000..65be7ae --- /dev/null +++ b/prisma/migrations/20250304095229_add_user_order/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "order" INTEGER NOT NULL DEFAULT 0; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 73261b6..ed49c61 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,11 +13,13 @@ model User { password String displayName String avatarUrl String + bio String? isAdmin Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt permissions Json articles Article[] + order Int @default(0) } model Article { diff --git a/src/routes/articles/controllers/list.ts b/src/routes/articles/controllers/list.ts index f6b9ae5..8b7ffac 100644 --- a/src/routes/articles/controllers/list.ts +++ b/src/routes/articles/controllers/list.ts @@ -55,6 +55,7 @@ export async function listArticles(req: Request, res: Response) { articles, totalPages: Math.ceil(total / perPage), currentPage: page, + total: total }); } catch (error) { // Логируем ошибку и отправляем ответ с кодом 500 diff --git a/src/routes/articles/controllers/search.ts b/src/routes/articles/controllers/search.ts index effa032..919e1bf 100644 --- a/src/routes/articles/controllers/search.ts +++ b/src/routes/articles/controllers/search.ts @@ -4,22 +4,26 @@ import { Prisma } from '@prisma/client'; export async function searchArticles(req: Request, res: Response) { try { - const { q, page = 1, limit = 9 } = req.query; + const { q, author, page = 1, limit = 9 } = req.query; const skip = ((Number(page) || 1) - 1) * (Number(limit) || 9); - // Определение where с явной обработкой q - const where : Prisma.ArticleWhereInput = - typeof q === 'string' && q.trim() - ? { - OR: [ - { title: { contains: q, mode: 'insensitive' } }, - { excerpt: { contains: q, mode: 'insensitive' } }, - { content: { contains: q, mode: 'insensitive' } }, - ], - } - : {}; + // Формируем where-условие + const where: Prisma.ArticleWhereInput = { + ...(typeof q === 'string' && q.trim() + ? { + OR: [ + { title: { contains: q, mode: 'insensitive' } }, + { excerpt: { contains: q, mode: 'insensitive' } }, + { content: { contains: q, mode: 'insensitive' } }, + ], + } + : {}), + ...(typeof author === 'string' && author.trim() + ? { authorId: author } + : {}), + }; - // Выполнение запросов + // Выполнение запроса const [articles, total] = await Promise.all([ prisma.article.findMany({ where, @@ -47,7 +51,7 @@ export async function searchArticles(req: Request, res: Response) { currentPage: Number(page) || 1, }); } catch (error) { - console.error('Error during article search:', error); - res.status(500).json({ error: 'Server error' }); + console.error('Ошибка поиска по статьям:', error); + res.status(500).json({ error: 'Серверная ошибка' }); } } diff --git a/src/routes/authors/controllers/authors.ts b/src/routes/authors/controllers/authors.ts new file mode 100644 index 0000000..7d46e38 --- /dev/null +++ b/src/routes/authors/controllers/authors.ts @@ -0,0 +1,14 @@ +import { Response } from 'express'; +import { AuthRequest } from '../../../middleware/auth'; +import { userService } from '../../../services/userService'; + + +// Список авторов - без permissions и без чистых админов +export async function getAuthors(req: AuthRequest, res: Response): Promise { + try { + const authors = await userService.getAuthors(); + res.json(authors); + } catch { + res.status(500).json({ error: 'Server error' }); + } +} diff --git a/src/routes/authors/index.ts b/src/routes/authors/index.ts new file mode 100644 index 0000000..10e406c --- /dev/null +++ b/src/routes/authors/index.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { getAuthors } from './controllers/authors'; + +const router = express.Router(); + + +router.get('/', getAuthors); + +export default router; \ No newline at end of file diff --git a/src/routes/users/controllers/users.ts b/src/routes/users/controllers/users.ts index 5c55108..f62773d 100644 --- a/src/routes/users/controllers/users.ts +++ b/src/routes/users/controllers/users.ts @@ -7,46 +7,46 @@ import { prisma } from '../../../lib/prisma'; import { getDefaultPermissions } from '../../../utils/permissions'; +// Список пользователей export async function getUsers(req: AuthRequest, res: Response): Promise { try { // Проверка прав администратора if (!req.user?.permissions.isAdmin) { - res.status(403).json({ error: 'Admin access required' }); + res.status(403).json({ error: 'Требуются права администратора' }); return } - // Получение списка пользователей const users = await userService.getUsers(); res.json(users); } catch { - res.status(500).json({ error: 'Server error' }); + res.status(500).json({ error: 'Серверная ошибка' }); } } export async function createUser(req: AuthRequest, res: Response): Promise { try { if (!req.user?.permissions.isAdmin) { - logger.warn(`Non-admin user ${req.user?.id} attempted to create user`); - res.status(403).json({ error: 'Admin access required' }); + logger.warn(`Не администратор ${req.user?.id} пытается создать пользователя`); + res.status(403).json({ error: 'Требуются права администратора' }); return } const { email, password, displayName, avatarUrl } = req.body; - // Check if user exists + // Проверка существования пользователя const existingUser = await prisma.user.findUnique({ where: { email } }); if (existingUser) { - res.status(400).json({ error: 'User already exists' }); + res.status(400).json({ error: 'Пользователь уже существует' }); return } - // Hash password + // Вычисление хеша пароля const hashedPassword = await bcrypt.hash(password, 10); - // Create user with default permissions + // Создание пользователя с правами по умолчанию const user = await prisma.user.create({ data: { email, @@ -64,48 +64,49 @@ export async function createUser(req: AuthRequest, res: Response): Promise } }); - logger.info(`User created successfully: ${user.id}`); + logger.info(`Успашное создание пользователя: ${user.id}`); res.status(201).json(user); } catch (error) { - logger.error('Error creating user:', error); - res.status(500).json({ error: 'Failed to create user' }); + logger.error('Ошибка создания пользователя:', error); + res.status(500).json({ error: 'Ошибка создания пользователя' }); } } export async function updateUser(req: AuthRequest, res: Response): Promise { try { if (!req.user?.permissions.isAdmin) { - logger.warn(`Non-admin user ${req.user?.id} attempted to update user`); - res.status(403).json({ error: 'Admin access required' }); + logger.warn(`Не администратор ${req.user?.id} пытается обновить пользователя`); + res.status(403).json({ error: 'Требуются права администратора' }); return } const { id } = req.params; - const { email, password, displayName, avatarUrl } = req.body; + const { email, password, displayName, bio, avatarUrl } = req.body; - // Check if user exists + // Проверка существования пользователя const existingUser = await prisma.user.findUnique({ where: { id } }); if (!existingUser) { - res.status(404).json({ error: 'User not found' }); + res.status(404).json({ error: 'Пользователь не найден' }); return } - // Prepare update data + // Подготовка данных для изменения const updateData: any = { email, displayName, + bio, avatarUrl }; - // Only update password if provided + // Если требуется сменить пароль if (password) { updateData.password = await bcrypt.hash(password, 10); } - // Update user + // Обновление данных пользователя const user = await prisma.user.update({ where: { id }, data: updateData, @@ -113,24 +114,26 @@ export async function updateUser(req: AuthRequest, res: Response): Promise id: true, email: true, displayName: true, + bio: true, avatarUrl: true, permissions: true } }); - logger.info(`User updated successfully: ${user.id}`); + logger.info(`Успешное обновление пользователя: ${user.id}`); res.json(user); } catch (error) { - logger.error('Error updating user:', error); - res.status(500).json({ error: 'Failed to update user' }); + logger.error('Ошибка обновления пользователя:', error); + res.status(500).json({ error: 'Ошибка обновления пользователя' }); } } +// Обновление прав пользователя export async function updateUserPermissions(req: AuthRequest, res: Response): Promise { try { // Проверка прав администратора if (!req.user?.permissions.isAdmin) { - res.status(403).json({ error: 'Admin access required' }); + res.status(403).json({ error: 'Требуются права администратора' }); return } @@ -142,15 +145,16 @@ export async function updateUserPermissions(req: AuthRequest, res: Response): Pr const user = await userService.updateUserPermissions(id, permissions); res.json(user); } catch { - res.status(500).json({ error: 'Server error' }); + res.status(500).json({ error: 'Серверная ошибка' }); } } +// Удаление пользователя export async function deleteUser(req: AuthRequest, res: Response): Promise { try { if (!req.user?.permissions.isAdmin) { - logger.warn(`Non-admin user ${req.user?.id} attempted to delete user`); - res.status(403).json({ error: 'Admin access required' }); + logger.warn(`Не администратор ${req.user?.id} пытается удалить пользователя`); + res.status(403).json({ error: 'Требуются права администратора' }); return } @@ -162,7 +166,7 @@ export async function deleteUser(req: AuthRequest, res: Response): Promise }); if (!existingUser) { - res.status(404).json({ error: 'User not found' }); + res.status(404).json({ error: 'Пользователь не найден' }); return } @@ -171,10 +175,10 @@ export async function deleteUser(req: AuthRequest, res: Response): Promise where: { id } }); - logger.info(`User deleted successfully: ${id}`); - res.json({ message: 'User deleted successfully' }); + logger.info(`Успешное удаление пользователя: ${id}`); + res.json({ message: 'Успешное удаление пользователя' }); } catch (error) { - logger.error('Error deleting user:', error); - res.status(500).json({ error: 'Failed to delete user' }); + logger.error('Ошибка удаления пользователя:', error); + res.status(500).json({ error: 'Ошибка удаления пользователя' }); } } \ No newline at end of file diff --git a/src/routes/users/index.ts b/src/routes/users/index.ts index 3b872b8..58684c0 100644 --- a/src/routes/users/index.ts +++ b/src/routes/users/index.ts @@ -5,7 +5,7 @@ import { createUser, updateUser, updateUserPermissions, - deleteUser + deleteUser, } from './controllers/users'; const router = express.Router(); diff --git a/src/server.ts b/src/server.ts index 5069884..beeca55 100644 --- a/src/server.ts +++ b/src/server.ts @@ -10,6 +10,7 @@ import userRoutes from './routes/users/index'; import articleRoutes from './routes/articles/index'; import galleryRoutes from './routes/gallery/index'; import imagesRoutes from './routes/images/index'; +import authorRoutes from './routes/authors/index'; const app = express(); @@ -33,6 +34,7 @@ app.use(errorLogger); // Маршруты app.use('/api/auth', authRoutes); app.use('/api/users', userRoutes); +app.use('/api/authors', authorRoutes); app.use('/api/articles', articleRoutes); app.use('/api/gallery', galleryRoutes); app.use('/api/images', imagesRoutes); diff --git a/src/services/userService.ts b/src/services/userService.ts index 3a76931..7b3ad77 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -1,5 +1,5 @@ import { PrismaClient } from '@prisma/client'; -import { User, UserPermissions } from '../types/auth'; +import { Author, User, UserPermissions } from '../types/auth'; import { JsonValue } from '@prisma/client/runtime/library'; const prisma = new PrismaClient(); @@ -12,6 +12,7 @@ export const userService = { email: string; displayName: string; avatarUrl: string; + bio: string | null; permissions: JsonValue; }> = await prisma.user.findMany({ select: { @@ -19,6 +20,7 @@ export const userService = { email: true, displayName: true, avatarUrl: true, + bio: true, permissions: true, }, }); @@ -40,6 +42,40 @@ export const userService = { throw new Error('Failed to fetch users'); } }, + + getAuthors: async (): Promise => { + try { + const authors = await prisma.user.findMany({ + select: { + id: true, + email: true, + displayName: true, + avatarUrl: true, + bio: true, + _count: { + select: { articles: true }, // Подсчёт количества статей автора + }, + }, + where: { order: { not: 0 } }, + orderBy: { + order: 'asc', + }, + }); + + return authors.map(author => ({ + id: author.id, + email: author.email, + displayName: author.displayName, + avatarUrl: author.avatarUrl, + bio: author.bio, + articlesCount: author._count.articles, // Количество статей + })); + } catch (error) { + console.error('Ошибка получения авторов:', error); + throw new Error('Ошибка получения авторов'); + } + }, + updateUserPermissions: async ( userId: string, permissions: User['permissions'] diff --git a/src/types/auth.ts b/src/types/auth.ts index c4800b2..552f460 100644 --- a/src/types/auth.ts +++ b/src/types/auth.ts @@ -13,5 +13,15 @@ export interface User { email: string; displayName: string; avatarUrl: string; + bio: string; permissions: UserPermissions; +} + +export interface Author { + id: string; + email: string; + displayName: string; + avatarUrl: string; + articlesCount: number; + bio: string | null; } \ No newline at end of file