diff --git a/README.md b/README.md index 8f0f65f..be93a46 100644 --- a/README.md +++ b/README.md @@ -96,3 +96,10 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors ## License Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). + + +## TODO + +-fix picture upload +-implement picture loader +-use leaflet for map implementation \ No newline at end of file diff --git a/client/favicon.ico b/assets/client/favicon.ico similarity index 100% rename from client/favicon.ico rename to assets/client/favicon.ico diff --git a/client/index.html b/assets/client/index.html similarity index 100% rename from client/index.html rename to assets/client/index.html diff --git a/client/main-EAY5NMSD.js b/assets/client/main-EAY5NMSD.js similarity index 100% rename from client/main-EAY5NMSD.js rename to assets/client/main-EAY5NMSD.js diff --git a/client/polyfills-B6TNHZQ6.js b/assets/client/polyfills-B6TNHZQ6.js similarity index 100% rename from client/polyfills-B6TNHZQ6.js rename to assets/client/polyfills-B6TNHZQ6.js diff --git a/client/styles-5INURTSO.css b/assets/client/styles-5INURTSO.css similarity index 100% rename from client/styles-5INURTSO.css rename to assets/client/styles-5INURTSO.css diff --git a/assets/uploads/profileImage-1755863111419-288148730.png b/assets/uploads/profileImage-1755863111419-288148730.png new file mode 100644 index 0000000..b610509 Binary files /dev/null and b/assets/uploads/profileImage-1755863111419-288148730.png differ diff --git a/assets/uploads/profileImage-1755863470216-89904139.png b/assets/uploads/profileImage-1755863470216-89904139.png new file mode 100644 index 0000000..b610509 Binary files /dev/null and b/assets/uploads/profileImage-1755863470216-89904139.png differ diff --git a/assets/uploads/profileImage-1755863741914-819946356.png b/assets/uploads/profileImage-1755863741914-819946356.png new file mode 100644 index 0000000..7b81a8b Binary files /dev/null and b/assets/uploads/profileImage-1755863741914-819946356.png differ diff --git a/assets/uploads/profileImage-1755875957454-185748963.png b/assets/uploads/profileImage-1755875957454-185748963.png new file mode 100644 index 0000000..834b6ce Binary files /dev/null and b/assets/uploads/profileImage-1755875957454-185748963.png differ diff --git a/assets/uploads/profileImage-1755934599888-605173062.png b/assets/uploads/profileImage-1755934599888-605173062.png new file mode 100644 index 0000000..7b81a8b Binary files /dev/null and b/assets/uploads/profileImage-1755934599888-605173062.png differ diff --git a/assets/uploads/profileImage-1755937934434-116283125.jpg b/assets/uploads/profileImage-1755937934434-116283125.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1755937934434-116283125.jpg differ diff --git a/assets/uploads/profileImage-1755946669630-126189068.png b/assets/uploads/profileImage-1755946669630-126189068.png new file mode 100644 index 0000000..8b96369 Binary files /dev/null and b/assets/uploads/profileImage-1755946669630-126189068.png differ diff --git a/assets/uploads/profileImage-1755947001183-889093929.png b/assets/uploads/profileImage-1755947001183-889093929.png new file mode 100644 index 0000000..8b96369 Binary files /dev/null and b/assets/uploads/profileImage-1755947001183-889093929.png differ diff --git a/assets/uploads/profileImage-1755953728360-115123395.jpg b/assets/uploads/profileImage-1755953728360-115123395.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1755953728360-115123395.jpg differ diff --git a/assets/uploads/profileImage-1755956983627-613659928.jpg b/assets/uploads/profileImage-1755956983627-613659928.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1755956983627-613659928.jpg differ diff --git a/assets/uploads/profileImage-1755958734546-548952268.jpg b/assets/uploads/profileImage-1755958734546-548952268.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1755958734546-548952268.jpg differ diff --git a/assets/uploads/profileImage-1756035516571-888403744.png b/assets/uploads/profileImage-1756035516571-888403744.png new file mode 100644 index 0000000..8b96369 Binary files /dev/null and b/assets/uploads/profileImage-1756035516571-888403744.png differ diff --git a/assets/uploads/profileImage-1756039374205-906295223.png b/assets/uploads/profileImage-1756039374205-906295223.png new file mode 100644 index 0000000..8b96369 Binary files /dev/null and b/assets/uploads/profileImage-1756039374205-906295223.png differ diff --git a/assets/uploads/profileImage-1756040589088-221180619.png b/assets/uploads/profileImage-1756040589088-221180619.png new file mode 100644 index 0000000..8b96369 Binary files /dev/null and b/assets/uploads/profileImage-1756040589088-221180619.png differ diff --git a/assets/uploads/profileImage-1756040607074-717639494.png b/assets/uploads/profileImage-1756040607074-717639494.png new file mode 100644 index 0000000..8b96369 Binary files /dev/null and b/assets/uploads/profileImage-1756040607074-717639494.png differ diff --git a/assets/uploads/profileImage-1756043249603-388360106.png b/assets/uploads/profileImage-1756043249603-388360106.png new file mode 100644 index 0000000..1d97ed5 Binary files /dev/null and b/assets/uploads/profileImage-1756043249603-388360106.png differ diff --git a/assets/uploads/profileImage-1756043320066-238311781.jpg b/assets/uploads/profileImage-1756043320066-238311781.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1756043320066-238311781.jpg differ diff --git a/assets/uploads/profileImage-1756043639121-64840711.png b/assets/uploads/profileImage-1756043639121-64840711.png new file mode 100644 index 0000000..060c1d4 Binary files /dev/null and b/assets/uploads/profileImage-1756043639121-64840711.png differ diff --git a/assets/uploads/profileImage-1756043684612-599737597.jpg b/assets/uploads/profileImage-1756043684612-599737597.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1756043684612-599737597.jpg differ diff --git a/assets/uploads/profileImage-1756043988693-513713031.jpg b/assets/uploads/profileImage-1756043988693-513713031.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1756043988693-513713031.jpg differ diff --git a/assets/uploads/profileImage-1756044017376-178693076.jpg b/assets/uploads/profileImage-1756044017376-178693076.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1756044017376-178693076.jpg differ diff --git a/assets/uploads/profileImage-1756044044280-781994271.jpg b/assets/uploads/profileImage-1756044044280-781994271.jpg new file mode 100644 index 0000000..7a3b4c1 Binary files /dev/null and b/assets/uploads/profileImage-1756044044280-781994271.jpg differ diff --git a/assets/uploads/profileImage-1756044615444-224911673.png b/assets/uploads/profileImage-1756044615444-224911673.png new file mode 100644 index 0000000..7b81a8b Binary files /dev/null and b/assets/uploads/profileImage-1756044615444-224911673.png differ diff --git a/nest-cli.json b/nest-cli.json index f9aa683..545b41e 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -3,6 +3,13 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { - "deleteOutDir": true + "deleteOutDir": true, + "assets": [ + { + "include": "../assets/**", + "watchAssets": true, + "outDir": "dist/assets" + } + ] } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f8cd15f..90a87d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,14 @@ "dependencies": { "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", - "@nestjs/platform-express": "^11.0.1", + "@nestjs/platform-express": "^11.1.6", + "@nestjs/platform-socket.io": "^11.1.6", "@nestjs/serve-static": "^5.0.3", "@nestjs/swagger": "^11.2.0", + "@nestjs/websockets": "^11.1.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", + "multer": "^2.0.2", "prisma": "^6.14.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" @@ -29,6 +32,7 @@ "@prisma/client": "^6.14.0", "@types/express": "^5.0.0", "@types/jest": "^30.0.0", + "@types/multer": "^2.0.0", "@types/node": "^22.10.7", "@types/supertest": "^6.0.2", "eslint": "^9.18.0", @@ -2425,6 +2429,25 @@ "@nestjs/core": "^11.0.0" } }, + "node_modules/@nestjs/platform-socket.io": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.6.tgz", + "integrity": "sha512-ozm+OKiRiFLNQdFLA3ULDuazgdVaPrdRdgtG/+404T7tcROXpbUuFL0eEmWJpG64CxMkBNwamclUSH6J0AeU7A==", + "license": "MIT", + "dependencies": { + "socket.io": "4.8.1", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "rxjs": "^7.1.0" + } + }, "node_modules/@nestjs/schematics": { "version": "11.0.7", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.7.tgz", @@ -2530,6 +2553,29 @@ } } }, + "node_modules/@nestjs/websockets": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.6.tgz", + "integrity": "sha512-jlBX5QpqhfEVfxkwxTesIjgl0bdhgFMoORQYzjRg1i+Z+Qouf4KmjNPv5DZE3DZRDg91E+3Bpn0VgW0Yfl94ng==", + "license": "MIT", + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-socket.io": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } + } + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -2745,6 +2791,12 @@ "@sinonjs/commons": "^3.0.1" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -2887,6 +2939,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -3007,11 +3068,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "22.17.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.2.tgz", "integrity": "sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -4206,6 +4276,15 @@ ], "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -5167,6 +5246,95 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -8132,6 +8300,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -9175,6 +9352,141 @@ "node": ">=8" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -10152,7 +10464,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { @@ -10664,6 +10975,27 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 9b61dd7..2061d81 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,14 @@ "dependencies": { "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", - "@nestjs/platform-express": "^11.0.1", + "@nestjs/platform-express": "^11.1.6", + "@nestjs/platform-socket.io": "^11.1.6", "@nestjs/serve-static": "^5.0.3", "@nestjs/swagger": "^11.2.0", + "@nestjs/websockets": "^11.1.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", + "multer": "^2.0.2", "prisma": "^6.14.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" @@ -40,6 +43,7 @@ "@prisma/client": "^6.14.0", "@types/express": "^5.0.0", "@types/jest": "^30.0.0", + "@types/multer": "^2.0.0", "@types/node": "^22.10.7", "@types/supertest": "^6.0.2", "eslint": "^9.18.0", diff --git a/prisma/migrations/20250819161859_/migration.sql b/prisma/migrations/20250819161859_/migration.sql new file mode 100644 index 0000000..f3b7e29 --- /dev/null +++ b/prisma/migrations/20250819161859_/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to drop the column `text` on the `Candidate` table. All the data in the column will be lost. + - Added the required column `justification` to the `Candidate` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "public"."Candidate" DROP COLUMN "text", +ADD COLUMN "justification" TEXT NOT NULL, +ALTER COLUMN "hobbies" DROP NOT NULL, +ALTER COLUMN "image" DROP NOT NULL; diff --git a/prisma/migrations/20250822104249_init/migration.sql b/prisma/migrations/20250822104249_init/migration.sql new file mode 100644 index 0000000..b0fb4fc --- /dev/null +++ b/prisma/migrations/20250822104249_init/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `image` on the `Candidate` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "public"."Candidate" DROP COLUMN "image", +ADD COLUMN "profileImage" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 02e2fc5..58ba974 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,14 +15,14 @@ datasource db { } model Candidate { - id Int @id @default(autoincrement()) - fullName String - email String - phoneNumber String - age Int - cityOrRegion String - hobbies String - text String - image String - createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + fullName String + email String + phoneNumber String + age Int + cityOrRegion String + hobbies String? + justification String + profileImage String? + createdAt DateTime @default(now()) } diff --git a/src/app.controller.ts b/src/app.controller.ts index 08d52fa..6737f51 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,12 +1,17 @@ -import { BadRequestException, Body, Controller, Get, NotFoundException, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { BadRequestException, Body, Controller, Get, NotFoundException, Param, ParseIntPipe, Post, Put, UploadedFile, UseInterceptors } from '@nestjs/common'; import { RegisterDto } from './dto/register.dto'; import { ApiTags } from '@nestjs/swagger'; import { PrismaService } from './services/prisma.service'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { diskStorage } from 'multer'; +import { extname } from 'path'; +import { AppGetaway } from './app.getaway'; @ApiTags('App') @Controller('app') export class AppController { - constructor(private prisma: PrismaService) { } + constructor(private prisma: PrismaService, private socketService: AppGetaway) { } + @Get('candidates') getCandidateList() { @@ -14,30 +19,80 @@ export class AppController { select: { id: true, fullName: true, - image: true, + age: true, + cityOrRegion: true, + profileImage: true, } }) } @Get('candidate/:id') getCandidateDetails(@Param('id', ParseIntPipe) id: number) { - return this.prisma.candidate.findFirst({where: {id : id}}); + return this.prisma.candidate.findFirst({ where: { id: id } }); } @Post('register') - register(@Body() dto: RegisterDto) { - return this.prisma.candidate.create({ data: { ...dto, createdAt: new Date() } }); + @UseInterceptors(FileInterceptor('profileImage', { + storage: diskStorage({ + destination: 'assets/uploads', + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); + const ext = extname(file.originalname); + cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`); + } + }) + })) + async register( + @UploadedFile() file: Express.Multer.File, + @Body() dto: RegisterDto + ) { + const imagePath = file ? file.filename : null; + const savedCandidate = await this.prisma.candidate.create({ + data: { + ...dto, + profileImage: imagePath, + createdAt: new Date(), + }, + }); + this.socketService.onAddCandidate(savedCandidate); + + return savedCandidate; } @Put('candidate/:id') - async update(@Param('id', ParseIntPipe) id: number, @Body() dto: RegisterDto) { - const candidate = await this.prisma.candidate.findFirst({ where: { id: id } }); - if (!candidate) throw new NotFoundException(`candidate with id ${id} not found`); + @UseInterceptors(FileInterceptor('profileImage', { + storage: diskStorage({ + destination: 'assets/uploads', + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); + const ext = extname(file.originalname); + cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`); + } + }) + })) + async update( + @Param('id', ParseIntPipe) id: number, + @UploadedFile() file: Express.Multer.File, + @Body() dto: RegisterDto + ) { + const candidate = await this.prisma.candidate.findFirst({ where: { id } }); + if (!candidate) throw new NotFoundException(`Candidate with id ${id} not found`); + const dayMilliseconds = 86400000; const expirationDate = new Date(candidate.createdAt.valueOf() + 3 * dayMilliseconds); - if (expirationDate < new Date()) throw new BadRequestException(`can not edit candidate form after 3 days`); - await this.prisma.candidate.update({ where: { id: id }, data: { id, ...dto } }); + if (expirationDate < new Date()) throw new BadRequestException(`Cannot edit candidate form after 3 days`); + + const profileImagePath = file ? file.filename : candidate.profileImage; + + const updatedCandidate = await this.prisma.candidate.update({ + where: { id }, + data: { + ...dto, + profileImage: profileImagePath, + }, + }); + this.socketService.onUpdateCandidate(updatedCandidate); + return; } - } diff --git a/src/app.getaway.ts b/src/app.getaway.ts new file mode 100644 index 0000000..7f833cf --- /dev/null +++ b/src/app.getaway.ts @@ -0,0 +1,26 @@ +import { MessageBody, OnGatewayConnection, OnGatewayDisconnect, SubscribeMessage, WebSocketGateway, WebSocketServer } from "@nestjs/websockets"; +import { Server, Socket } from "socket.io"; + +@WebSocketGateway({ + cors: true +}) + +export class AppGetaway implements OnGatewayDisconnect, OnGatewayConnection { + @WebSocketServer() server: Server + + handleDisconnect(client: Socket) { + console.log(`${client.id} disconnected`) + } + + handleConnection(client: Socket, ...args: any[]) { + console.log(`${client.id} connected`) + } + + onAddCandidate(registrationData: any) { + this.server.emit('candidateRegistered', registrationData); + } + + onUpdateCandidate(updatedData: any) { + this.server.emit('candidateUpdated', updatedData); + } +} \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index dc1def4..577c7fb 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,14 +3,21 @@ import { AppController } from './app.controller'; import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; import { PrismaService } from './services/prisma.service'; +import { AppGetaway } from './app.getaway'; @Module({ imports: [ - ServeStaticModule.forRoot({ - rootPath: join(__dirname, 'client'), - }), + ServeStaticModule.forRoot( + { + rootPath: join(__dirname, 'assets/client'), + renderPath: '/' + }, + { + rootPath: join(__dirname, 'assets/uploads'), + serveRoot: '/uploads', + }), ], controllers: [AppController], - providers: [PrismaService], + providers: [PrismaService, AppGetaway], }) export class AppModule { } diff --git a/src/dto/register.dto.ts b/src/dto/register.dto.ts index 9a85932..41796b8 100644 --- a/src/dto/register.dto.ts +++ b/src/dto/register.dto.ts @@ -1,22 +1,27 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsInt } from 'class-validator'; +import { IsInt, IsOptional, IsString } from 'class-validator'; export class RegisterDto { @ApiProperty({ required: true }) + @IsString() fullName: string; @ApiProperty({ required: true }) + @IsString() email: string; @ApiProperty({ required: true }) + @IsString() phoneNumber: string; @ApiProperty({ required: true }) @IsInt() age: number; @ApiProperty({ required: true }) + @IsString() cityOrRegion: string; @ApiProperty({ required: false }) - hobbies: string; - @ApiProperty({ required: false }) - text: string; - @ApiProperty({ required: false }) - image: string; + @IsString() + @IsOptional() + hobbies?: string; + @ApiProperty({ required: true }) + @IsString() + justification: string; } diff --git a/src/main.ts b/src/main.ts index e9b0260..a1fa38a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,7 +14,7 @@ async function bootstrap() { ); setupSwagger(app); app.enableCors({ - origin: true, + origin: [true], methods: [ 'GET', 'HEAD', diff --git a/tsconfig.json b/tsconfig.json index 57f9635..b2e7769 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,6 @@ "forceConsistentCasingInFileNames": true, "noImplicitAny": true, "strictBindCallApply": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, } -} +} \ No newline at end of file