From 181f3e061a8879c0546ac4336871d8afb18f3245 Mon Sep 17 00:00:00 2001 From: ramzini22 Date: Sat, 27 Sep 2025 16:40:00 +0800 Subject: [PATCH] feat: delete webdav and add hash for file --- api/swagger.json | 37 ++- package-lock.json | 287 ++---------------- package.json | 4 +- .../files/controllers/files.controller.ts | 24 +- src/modules/files/dto/upload.dto.ts | 8 + src/modules/files/files.module.ts | 18 +- src/modules/files/services/files.service.ts | 67 ++-- 7 files changed, 118 insertions(+), 327 deletions(-) create mode 100644 src/modules/files/dto/upload.dto.ts diff --git a/api/swagger.json b/api/swagger.json index 382227c..e8624ec 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -5,6 +5,16 @@ "post": { "operationId": "FilesController_upload", "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadDto" + } + } + } + }, "responses": { "201": { "description": "" @@ -15,12 +25,21 @@ ] } }, - "/files/{id}": { + "/files/{chatId}/{fileId}": { "get": { "operationId": "FilesController_downFile", "parameters": [ { - "name": "id", + "name": "chatId", + "required": true, + "in": "path", + "description": "Chat ID", + "schema": { + "type": "string" + } + }, + { + "name": "fileId", "required": true, "in": "path", "description": "File ID", @@ -66,6 +85,18 @@ } ], "components": { - "schemas": {} + "schemas": { + "UploadDto": { + "type": "object", + "properties": { + "chatId": { + "type": "string" + } + }, + "required": [ + "chatId" + ] + } + } } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3e942d9..a8e98ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,11 +20,9 @@ "class-validator": "^0.14.1", "dotenv": "^16.4.5", "kafkajs": "^2.2.4", - "nestjs-webdav": "^1.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "uuid": "^11.1.0", - "webdav": "^4.6.0" + "uuid": "^11.1.0" }, "devDependencies": { "@nestjs/cli": "^10.4.9", @@ -2484,12 +2482,6 @@ "node": ">= 0.4" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -2526,29 +2518,12 @@ "fastq": "^1.17.1" } }, - "node_modules/axios": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.30.1.tgz", - "integrity": "sha512-2XabsR1u0/B6OoKy57/xJmPkQiUvdoV93oW4ww+Xjee7C2er/O5U77lvqycDkT2VQDtfjYcjw8ZV8GDaoqwjHQ==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, "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==", "license": "MIT" }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2739,12 +2714,6 @@ "node": ">=10.16.0" } }, - "node_modules/byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/byte-length/-/byte-length-1.0.2.tgz", - "integrity": "sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q==", - "license": "MIT" - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2778,6 +2747,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "devOptional": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2858,15 +2828,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3079,18 +3040,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -3254,15 +3203,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -3402,15 +3342,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -3495,6 +3426,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "devOptional": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3651,6 +3583,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3660,6 +3593,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3676,6 +3610,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "devOptional": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3688,7 +3623,10 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -4480,24 +4418,6 @@ "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", "license": "MIT" }, - "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.1.1" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastify": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.28.1.tgz", @@ -4770,26 +4690,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -4873,22 +4773,6 @@ "node": "*" } }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4955,6 +4839,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5010,6 +4895,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "devOptional": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -5034,6 +4920,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "devOptional": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5189,6 +5076,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5277,6 +5165,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5289,7 +5178,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -5304,6 +5196,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "devOptional": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5312,21 +5205,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hot-patcher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-1.0.0.tgz", - "integrity": "sha512-3H8VH0PreeNsKMZw16nTHbUp4YoHCnPlawpsPXGJUR4qENDynl79b6Xk9CIFvLcH1qungBsCuzKcWyzoPPalTw==", - "license": "MIT" - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5618,12 +5496,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "license": "MIT" - }, "node_modules/is-bun-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", @@ -6161,12 +6033,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/layerr": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/layerr/-/layerr-0.1.2.tgz", - "integrity": "sha512-ob5kTd9H3S4GOG2nVXyQhOu9O8nBgP555XxWPkJI0tR0JeRilfyTp8WtPdIJHLXBmHMSdEq5+KMxiYABeScsIQ==", - "license": "MIT" - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6833,22 +6699,12 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "license": "BSD-3-Clause", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7135,23 +6991,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nested-property": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nested-property/-/nested-property-4.0.0.tgz", - "integrity": "sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==", - "license": "MIT" - }, - "node_modules/nestjs-webdav": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nestjs-webdav/-/nestjs-webdav-1.0.1.tgz", - "integrity": "sha512-wHV7/WomumuTiwETMHM+wWvIv2Vu1PHy4d2+H29Kh+5GH3md3AFEn5wkuq7ekiFMa1Qsiwytc0XdSMA56Zm/Dw==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": ">=6.7.0", - "@nestjs/core": ">=6.7.0", - "webdav": ">=3.0.0" - } - }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -7558,12 +7397,6 @@ "optional": true, "peer": true }, - "node_modules/path-posix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz", - "integrity": "sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==", - "license": "ISC" - }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -7844,12 +7677,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7876,12 +7703,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8092,12 +7913,6 @@ "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -9046,18 +8861,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/strtok3": { "version": "10.3.4", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", @@ -9792,22 +9595,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "license": "MIT" - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9889,42 +9676,6 @@ "defaults": "^1.0.3" } }, - "node_modules/webdav": { - "version": "4.11.5", - "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.11.5.tgz", - "integrity": "sha512-bGw+NvtGHLftrkXPZt/fMKTcfPYIfvMFUSHXjicMvDYvAxJTgd4txGaniQytRa/Tb9VmzxNmTyWfynAO+SVhsQ==", - "license": "MIT", - "dependencies": { - "axios": "^0.30.0", - "base-64": "^1.0.0", - "byte-length": "^1.0.2", - "fast-xml-parser": "^4.2.4", - "he": "^1.2.0", - "hot-patcher": "^1.0.0", - "layerr": "^0.1.2", - "md5": "^2.3.0", - "minimatch": "^5.1.0", - "nested-property": "^4.0.0", - "path-posix": "^1.0.0", - "url-join": "^4.0.1", - "url-parse": "^1.5.10" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/webdav/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 206ab60..a55d6c1 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,9 @@ "class-validator": "^0.14.1", "dotenv": "^16.4.5", "kafkajs": "^2.2.4", - "nestjs-webdav": "^1.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "uuid": "^11.1.0", - "webdav": "^4.6.0" + "uuid": "^11.1.0" }, "devDependencies": { "@nestjs/cli": "^10.4.9", diff --git a/src/modules/files/controllers/files.controller.ts b/src/modules/files/controllers/files.controller.ts index b041d80..906bb2b 100644 --- a/src/modules/files/controllers/files.controller.ts +++ b/src/modules/files/controllers/files.controller.ts @@ -1,9 +1,10 @@ -import { Controller, Get, Param, Post, Res, UploadedFile, UseInterceptors } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Res, UploadedFile, UseInterceptors } from '@nestjs/common'; import { ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; import { FastifyReply } from 'fastify'; import { FileInterceptor, File } from '@nest-lab/fastify-multer'; import { FilesService } from '../services/files.service'; import { DataResponse } from '../../../common/swagger/data-response.dto'; +import { UploadDto } from '../dto/upload.dto'; @ApiTags('Files') @Controller('files') @@ -12,11 +13,12 @@ export class FilesController { @Post('upload') @UseInterceptors(FileInterceptor('file')) // 👈 один файл - async upload(@UploadedFile() file: File): Promise> { - return await this.filesService.uploadFile(file); + async upload(@UploadedFile() file: File, @Body() body: UploadDto): Promise> { + return await this.filesService.uploadFile(file, body); } - @ApiParam({ name: 'id', type: String, description: 'File ID' }) + @ApiParam({ name: 'chatId', type: String, description: 'Chat ID' }) + @ApiParam({ name: 'fileId', type: String, description: 'File ID' }) @ApiResponse({ status: 200, description: 'Returns the file as Buffer', @@ -29,16 +31,8 @@ export class FilesController { }, }, }) - @Get(':id') - async downFile(@Param('id') id: string, @Res() reply: FastifyReply) { - const response = await this.filesService.getFileData(id); - - reply.headers({ - 'Content-Type': 'application/octet-stream', - 'Content-Disposition': `attachment; filename="${id}"`, - 'Content-Length': response.data.length, - }); - - reply.send(response.data); + @Get(':chatId/:fileId') + downFile(@Param('chatId') chatId: string, @Param('fileId') fileId: string, @Res() reply: FastifyReply) { + return this.filesService.downFile(chatId, fileId, reply); } } diff --git a/src/modules/files/dto/upload.dto.ts b/src/modules/files/dto/upload.dto.ts new file mode 100644 index 0000000..9dc795b --- /dev/null +++ b/src/modules/files/dto/upload.dto.ts @@ -0,0 +1,8 @@ +import { IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class UploadDto { + @ApiProperty() + @IsString() + readonly chatId?: string; +} diff --git a/src/modules/files/files.module.ts b/src/modules/files/files.module.ts index 42ccf68..59393d1 100644 --- a/src/modules/files/files.module.ts +++ b/src/modules/files/files.module.ts @@ -1,26 +1,10 @@ import { Module } from '@nestjs/common'; import { FastifyMulterModule } from '@nest-lab/fastify-multer'; -import { WebDAVModule } from 'nestjs-webdav'; - -import { Envs } from '../../common/envs/env'; import { FilesService } from './services/files.service'; import { FilesController } from './controllers/files.controller'; @Module({ - imports: [ - FastifyMulterModule, - WebDAVModule.forRootAsync({ - useFactory: () => { - return { - config: { - endpoint: `${Envs.webdav.host}:${Envs.webdav.port}`, - username: Envs.webdav.user, - password: Envs.webdav.password, - }, - }; - }, - }), - ], + imports: [FastifyMulterModule], controllers: [FilesController], providers: [FilesService], exports: [FilesService], diff --git a/src/modules/files/services/files.service.ts b/src/modules/files/services/files.service.ts index bd644d0..3ca9b25 100644 --- a/src/modules/files/services/files.service.ts +++ b/src/modules/files/services/files.service.ts @@ -1,43 +1,68 @@ +import { createReadStream, createWriteStream, promises as fs, statSync } from 'fs'; +import { dirname, join } from 'path'; +import * as process from 'process'; +import { createHash } from 'crypto'; import { Injectable } from '@nestjs/common'; -import { InjectWebDAV, WebDAV } from 'nestjs-webdav'; import { File } from '@nest-lab/fastify-multer'; -import { v4 as uuidv4 } from 'uuid'; +import { FastifyReply } from 'fastify'; import { DataResponse } from '../../../common/swagger/data-response.dto'; +import { UploadDto } from '../dto/upload.dto'; +import { logger } from '../../../common/logger/logger'; @Injectable() export class FilesService { - constructor( - // @ts-ignore - @InjectWebDAV() - private readonly webDav: WebDAV, - ) {} + private readonly STORAGE_ROOT = join(process.cwd(), 'data', 'files'); - async uploadFile(file: File): Promise> { + public async uploadFile(file: File, body: UploadDto): Promise> { try { - const filePath = uuidv4(); - await this.webDav.putFileContents(filePath, file.buffer); + // 1. Генерируем хэш содержимого + const hash = createHash('sha256').update(file.buffer!).digest('hex'); + const filePath = `/${body.chatId}/${hash}`; - return DataResponse.success(filePath); + try { + const stat = statSync(join(this.STORAGE_ROOT, filePath)); + if (stat.size === file.size) return DataResponse.success(filePath); + return this.saveFile(filePath, file.buffer!); + } catch (e) { + return this.saveFile(filePath, file.buffer!); + } } catch (e) { + logger.error(e); return DataResponse.error(`Failed to upload file: ${e.message}`); } } - async getFileData(id: string): Promise> { + public downFile(chatId: string, fileId: string, reply: FastifyReply) { try { - const buffer = (await this.webDav.getFileContents(id)) as Buffer; - return DataResponse.success(buffer); - } catch (e) { - return DataResponse.error(Buffer.alloc(0)); + const filePath = join(this.STORAGE_ROOT, `${chatId}/${fileId}`); + + const stat = statSync(filePath); + const stream = createReadStream(filePath); + + reply + .headers({ + 'Content-Type': 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${fileId}"`, + 'Content-Length': stat.size, + }) + .send(stream); + } catch (err) { + // если файл не найден + reply.status(404).send(DataResponse.error(`File not found`)); } } - encodeRFC5987ValueChars(str: string): string { - return encodeURIComponent(str) - .replace(/['()]/g, escape) + private async saveFile(filePath: string, buffer: Buffer): Promise> { + const absPath = join(this.STORAGE_ROOT, filePath); - .replace(/\*/g, '%2A') + await fs.mkdir(dirname(absPath), { recursive: true }); - .replace(/%(?:7C|60|5E)/g, unescape); + return new Promise((resolve, reject) => { + const stream = createWriteStream(absPath); + stream.write(buffer); + stream.end(); + stream.on('finish', () => resolve(DataResponse.success(filePath))); + stream.on('error', () => reject()); + }); } }