mirror of
https://github.com/StepanovPlaton/Chat.git
synced 2026-04-05 21:30:41 +04:00
Complete project
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
PORT=8000
|
||||||
DATABASE_TYPE=postgres
|
DATABASE_TYPE=postgres
|
||||||
DATABASE_HOST=localhost
|
DATABASE_HOST=localhost
|
||||||
DATABASE_PORT=3306
|
DATABASE_PORT=3306
|
||||||
|
|||||||
378
backend/package-lock.json
generated
378
backend/package-lock.json
generated
@@ -9,12 +9,15 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^11.0.1",
|
"@nestjs/common": "^11.0.11",
|
||||||
"@nestjs/config": "^4.0.1",
|
"@nestjs/config": "^4.0.1",
|
||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.11",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@nestjs/platform-socket.io": "^11.0.11",
|
||||||
|
"@nestjs/platform-ws": "^11.0.11",
|
||||||
"@nestjs/swagger": "^11.0.6",
|
"@nestjs/swagger": "^11.0.6",
|
||||||
"@nestjs/typeorm": "^11.0.0",
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
|
"@nestjs/websockets": "^11.0.11",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"mysql2": "^3.13.0",
|
"mysql2": "^3.13.0",
|
||||||
@@ -2407,6 +2410,65 @@
|
|||||||
"@nestjs/core": "^11.0.0"
|
"@nestjs/core": "^11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nestjs/platform-socket.io": {
|
||||||
|
"version": "11.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.0.11.tgz",
|
||||||
|
"integrity": "sha512-+bLrtTSPDX6AxrL9PbR5lEgcEnn6oFzkGpLUcm3Xs9x5OBejzJh1tiWgBGJRQIh3l9iIG8/mQ8hNwufAt8SIcA==",
|
||||||
|
"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/platform-ws": {
|
||||||
|
"version": "11.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/platform-ws/-/platform-ws-11.0.11.tgz",
|
||||||
|
"integrity": "sha512-aIQCEJVHwlRwJPczFShsWEJq7w+VqyAFe1EjG8oxSihJ7sPqT+1jOLgqXabZSAKZNl2sF4++NtxwGZG++5/hlQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.8.1",
|
||||||
|
"ws": "8.18.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/platform-ws/node_modules/ws": {
|
||||||
|
"version": "8.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
||||||
|
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
||||||
|
"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/@nestjs/schematics": {
|
"node_modules/@nestjs/schematics": {
|
||||||
"version": "11.0.2",
|
"version": "11.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.2.tgz",
|
||||||
@@ -2579,6 +2641,29 @@
|
|||||||
"typeorm": "^0.3.0"
|
"typeorm": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nestjs/websockets": {
|
||||||
|
"version": "11.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.0.11.tgz",
|
||||||
|
"integrity": "sha512-9sNNT/kYA534iaFyZ9MrOXKwQFuJArsMXhT6ywVxaWKQ84lVbV/sDmdmJUe9mzUGLPiHMn+m3oDUO9MiLTEKPA==",
|
||||||
|
"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/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -2710,6 +2795,12 @@
|
|||||||
"@sinonjs/commons": "^3.0.0"
|
"@sinonjs/commons": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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/@sqltools/formatter": {
|
"node_modules/@sqltools/formatter": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
||||||
@@ -3134,6 +3225,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/cors": {
|
||||||
|
"version": "2.8.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
|
||||||
|
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "9.6.1",
|
"version": "9.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||||
@@ -3276,7 +3376,6 @@
|
|||||||
"version": "22.13.10",
|
"version": "22.13.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.20.0"
|
"undici-types": "~6.20.0"
|
||||||
@@ -4372,6 +4471,15 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"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/bin-version": {
|
"node_modules/bin-version": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz",
|
||||||
@@ -5486,6 +5594,104 @@
|
|||||||
"node": ">= 0.8"
|
"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/cookie": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"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": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.18.1",
|
"version": "5.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
|
||||||
@@ -8725,6 +8931,15 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
@@ -10098,6 +10313,141 @@
|
|||||||
"node": ">=8"
|
"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/sort-keys": {
|
"node_modules/sort-keys": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
||||||
@@ -11414,7 +11764,6 @@
|
|||||||
"version": "6.20.0",
|
"version": "6.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
@@ -11910,6 +12259,27 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"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": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
|||||||
@@ -20,12 +20,15 @@
|
|||||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^11.0.1",
|
"@nestjs/common": "^11.0.11",
|
||||||
"@nestjs/config": "^4.0.1",
|
"@nestjs/config": "^4.0.1",
|
||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.11",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@nestjs/platform-socket.io": "^11.0.11",
|
||||||
|
"@nestjs/platform-ws": "^11.0.11",
|
||||||
"@nestjs/swagger": "^11.0.6",
|
"@nestjs/swagger": "^11.0.6",
|
||||||
"@nestjs/typeorm": "^11.0.0",
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
|
"@nestjs/websockets": "^11.0.11",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"mysql2": "^3.13.0",
|
"mysql2": "^3.13.0",
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import Message, { CreateMessageDTO } from '@/entities/message';
|
import Message, { CreateMessageDTO } from '@/entities/message';
|
||||||
|
import MessageGateway from '@/shared/gateways/message';
|
||||||
import MessageService from '@/shared/services/message';
|
import MessageService from '@/shared/services/message';
|
||||||
import { Body, Controller, Get, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Post } from '@nestjs/common';
|
||||||
|
|
||||||
@Controller('message')
|
@Controller('message')
|
||||||
export class MessageController {
|
export class MessageController {
|
||||||
constructor(private messageService: MessageService) {}
|
constructor(
|
||||||
|
private messageService: MessageService,
|
||||||
|
private messageGateway: MessageGateway,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async getTopOfHistory(): Promise<Message[]> {
|
async getTopOfHistory(): Promise<Message[]> {
|
||||||
@@ -13,6 +17,8 @@ export class MessageController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
async send(@Body() message: CreateMessageDTO): Promise<Message> {
|
async send(@Body() message: CreateMessageDTO): Promise<Message> {
|
||||||
return await this.messageService.addMessage(message);
|
const newMessage = await this.messageService.addMessage(message);
|
||||||
|
this.messageGateway.sendMessage(newMessage);
|
||||||
|
return newMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ export class Message {
|
|||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
text: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
timeOfSend: string;
|
timeOfSend: string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||||
import { ValidationPipe } from '@nestjs/common';
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
|
import { WsAdapter } from '@nestjs/platform-ws';
|
||||||
|
|
||||||
import AppModule from './modules/app';
|
import AppModule from '@/modules/app';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
app.useGlobalPipes(new ValidationPipe());
|
app.useGlobalPipes(new ValidationPipe());
|
||||||
|
app.useWebSocketAdapter(new WsAdapter(app));
|
||||||
|
|
||||||
const options = new DocumentBuilder()
|
const options = new DocumentBuilder()
|
||||||
.setTitle('Chat')
|
.setTitle('Chat')
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { ConfigModule } from '@nestjs/config';
|
|||||||
|
|
||||||
import config from '@/configuration/configuration';
|
import config from '@/configuration/configuration';
|
||||||
import databaseConfig from '@/configuration/database';
|
import databaseConfig from '@/configuration/database';
|
||||||
import MessageModule from '../message';
|
|
||||||
|
import MessageModule from '@/modules/message';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [config],
|
load: [config, databaseConfig],
|
||||||
}),
|
}),
|
||||||
TypeOrmModule.forRoot(databaseConfig()),
|
TypeOrmModule.forRoot(databaseConfig()),
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import MessageController from '@/controllers/message';
|
import MessageController from '@/controllers/message';
|
||||||
import Message from '@/entities/message';
|
import Message from '@/entities/message';
|
||||||
|
import MessageGateway from '@/shared/gateways/message';
|
||||||
import MessageService from '@/shared/services/message';
|
import MessageService from '@/shared/services/message';
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
@@ -7,6 +8,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([Message])],
|
imports: [TypeOrmModule.forFeature([Message])],
|
||||||
controllers: [MessageController],
|
controllers: [MessageController],
|
||||||
providers: [MessageService],
|
providers: [MessageService, MessageGateway],
|
||||||
})
|
})
|
||||||
export class MessageModule {}
|
export class MessageModule {}
|
||||||
|
|||||||
3
backend/src/shared/gateways/message/index.ts
Normal file
3
backend/src/shared/gateways/message/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { MessageGateway } from './message.gateway';
|
||||||
|
|
||||||
|
export default MessageGateway;
|
||||||
17
backend/src/shared/gateways/message/message.gateway.ts
Normal file
17
backend/src/shared/gateways/message/message.gateway.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Message from '@/entities/message';
|
||||||
|
import { OnGatewayConnection, WebSocketGateway } from '@nestjs/websockets';
|
||||||
|
import { Socket } from 'socket.io';
|
||||||
|
|
||||||
|
@WebSocketGateway(8002)
|
||||||
|
export class MessageGateway implements OnGatewayConnection {
|
||||||
|
private clients: Socket[] = [];
|
||||||
|
|
||||||
|
handleConnection(client: Socket) {
|
||||||
|
this.clients.push(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage = (message: Message) => {
|
||||||
|
console.log(message, this.clients.length);
|
||||||
|
this.clients.forEach((client) => client.send(JSON.stringify(message)));
|
||||||
|
};
|
||||||
|
}
|
||||||
50
docker-compose-all.yml
Normal file
50
docker-compose-all.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
container_name: postgres_container
|
||||||
|
image: postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: pass2postgres
|
||||||
|
PGDATA: /data/postgres
|
||||||
|
volumes:
|
||||||
|
- postgres:/data/postgres
|
||||||
|
- ./create_database.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
|
# ports:
|
||||||
|
# - "5432:5432"
|
||||||
|
networks:
|
||||||
|
- postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
environment:
|
||||||
|
DATABASE_HOST: postgres
|
||||||
|
DATABASE_PORT: 5432
|
||||||
|
DATABASE_USER: postgres
|
||||||
|
DATABASE_PASSWORD: pass2postgres
|
||||||
|
DATABASE_DATABASE: chat
|
||||||
|
WEBSOCKETS_PORT: 8001
|
||||||
|
# ports:
|
||||||
|
# - "5000:5000"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
environment:
|
||||||
|
NEXT_PUBLIC_BASE_PROTOCOL: http
|
||||||
|
NEXT_PUBLIC_BASE_DOMAIN: backend
|
||||||
|
NEXT_PUBLIC_BASE_PORT: 3000
|
||||||
|
NEXT_PUBLIC_WS_URL: ws://backend:8002/
|
||||||
|
NEXT_PUBLIC_API_PATTERN: /
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
networks:
|
||||||
|
postgres:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres:
|
||||||
@@ -1 +0,0 @@
|
|||||||
NEXT_PUBLIC_API_PATTERN=/api
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
BACKEND_PROTOCOL=http
|
BACKEND_API_PROTOCOL=http
|
||||||
BACKEND_DOMAIN=127.0.0.1
|
BACKEND_API_DOMAIN=localhost
|
||||||
BACKEND_PORT=8000
|
BACKEND_API_PORT=8000
|
||||||
|
|
||||||
BASE_PROTOCOL=http
|
NEXT_PUBLIC_BASE_PROTOCOL=http
|
||||||
BASE_DOMAIN=127.0.0.1
|
NEXT_PUBLIC_BASE_DOMAIN=localhost
|
||||||
BASE_PORT=3000
|
NEXT_PUBLIC_BASE_PORT=3000
|
||||||
|
|
||||||
NEXT_PUBLIC_BASE_URL=http://127.0.0.1:3000
|
NEXT_PUBLIC_WS_URL=ws://127.0.0.1:8002/
|
||||||
|
|
||||||
|
NEXT_PUBLIC_API_PATTERN=/api/
|
||||||
@@ -4,20 +4,21 @@ const nextConfig = {
|
|||||||
{
|
{
|
||||||
source: "/api/:path*",
|
source: "/api/:path*",
|
||||||
destination:
|
destination:
|
||||||
`${process.env.BACKEND_PROTOCOL}://` +
|
`${process.env.BACKEND_API_PROTOCOL}://` +
|
||||||
`${process.env.BACKEND_DOMAIN}:${process.env.BACKEND_PORT}/:path*`,
|
`${process.env.BACKEND_API_DOMAIN}:${process.env.BACKEND_API_PORT}/:path*`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
protocol: process.env.BASE_PROTOCOL,
|
protocol: process.env.NEXT_PUBLIC_BASE_PROTOCOL,
|
||||||
hostname: process.env.BASE_DOMAIN,
|
hostname: process.env.NEXT_PUBLIC_BASE_DOMAIN,
|
||||||
port: process.env.BASE_PORT,
|
port: process.env.NEXT_PUBLIC_BASE_PORT,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
devIndicators: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
1022
frontend/package-lock.json
generated
1022
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,14 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"next": "15.2.2",
|
"next": "15.2.2",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0",
|
||||||
|
"swr": "^2.3.3",
|
||||||
|
"uuid": "^11.1.0",
|
||||||
|
"yup": "^1.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
@@ -22,7 +26,7 @@
|
|||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.2.2",
|
"eslint-config-next": "15.2.2",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
const config = {
|
const config = {
|
||||||
plugins: ["@tailwindcss/postcss"],
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
@tailwind base;
|
/* @tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities; */
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
font-size: calc((100vw / 1920) * 20);
|
||||||
|
|
||||||
|
--color-col1: #fb4934;
|
||||||
|
--color-col2: #b8bb26;
|
||||||
|
--color-col3: #fabd2f;
|
||||||
|
--color-col4: #83a598;
|
||||||
|
--color-col5: #d3869b;
|
||||||
|
--color-col6: #8ec07c;
|
||||||
|
--color-col7: #a89986;
|
||||||
|
--color-col8: #fe8019;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
--color-bg0: #fbf1c7;
|
--color-bg0: #fbf1c7;
|
||||||
--color-bg1: #ebdbb2;
|
--color-bg1: #ebdbb2;
|
||||||
|
--color-bg2: #d5c4a1;
|
||||||
|
--color-bg3: #bdae93;
|
||||||
--color-bg4: #a89984;
|
--color-bg4: #a89984;
|
||||||
|
|
||||||
--color-fg0: #282828;
|
--color-fg0: #282828;
|
||||||
--color-fg1: #3c3836;
|
--color-fg1: #3c3836;
|
||||||
|
--color-fg2: #504945;
|
||||||
|
--color-fg3: #665c54;
|
||||||
--color-fg4: #7c6f64;
|
--color-fg4: #7c6f64;
|
||||||
|
|
||||||
--color-ac0: #83a598;
|
--color-ac0: #83a598;
|
||||||
@@ -16,18 +34,19 @@
|
|||||||
--color-ac2: #8ec07c;
|
--color-ac2: #8ec07c;
|
||||||
|
|
||||||
--color-err: #cc241d;
|
--color-err: #cc241d;
|
||||||
|
|
||||||
--app-width: 70%;
|
|
||||||
font-size: calc((100vw / 1920) * 20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
--color-bg0: #282828;
|
--color-bg0: #282828;
|
||||||
--color-bg1: #3c3836;
|
--color-bg1: #3c3836;
|
||||||
|
--color-bg2: #504945;
|
||||||
|
--color-bg3: #665c54;
|
||||||
--color-bg4: #7c6f64;
|
--color-bg4: #7c6f64;
|
||||||
|
|
||||||
--color-fg0: #fbf1c7;
|
--color-fg0: #fbf1c7;
|
||||||
--color-fg1: #ebdbb2;
|
--color-fg1: #ebdbb2;
|
||||||
|
--color-fg2: #d5c4a1;
|
||||||
|
--color-fg3: #bdae93;
|
||||||
--color-fg4: #a89984;
|
--color-fg4: #a89984;
|
||||||
|
|
||||||
--color-ac0: #076678;
|
--color-ac0: #076678;
|
||||||
@@ -35,8 +54,19 @@
|
|||||||
--color-ac2: #427b58;
|
--color-ac2: #427b58;
|
||||||
|
|
||||||
--color-err: #cc241d;
|
--color-err: #cc241d;
|
||||||
|
|
||||||
|
--color-col1: #9d0006;
|
||||||
|
--color-col2: #79740e;
|
||||||
|
--color-col3: #b57614;
|
||||||
|
--color-col4: #076678;
|
||||||
|
--color-col5: #8f3f71;
|
||||||
|
--color-col6: #427b58;
|
||||||
|
--color-col7: #7c6f65;
|
||||||
|
--color-col8: #af3a03;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -59,10 +89,6 @@ body * {
|
|||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio::-webkit-media-controls-panel {
|
|
||||||
background-color: var(--color-bg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
:root {
|
:root {
|
||||||
font-size: calc((100vw / 1920) * 56);
|
font-size: calc((100vw / 1920) * 56);
|
||||||
@@ -74,4 +100,4 @@ audio::-webkit-media-controls-panel {
|
|||||||
:root {
|
:root {
|
||||||
font-size: calc((100vw / 1920) * 64);
|
font-size: calc((100vw / 1920) * 64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Roboto } from "next/font/google";
|
import { Roboto } from "next/font/google";
|
||||||
import "./globals.css";
|
|
||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
const roboto = Roboto({ subsets: ["latin"] });
|
const roboto = Roboto({ subsets: ["latin"] });
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="ru" suppressHydrationWarning>
|
<html lang="ru" suppressHydrationWarning>
|
||||||
<body className={roboto.className}>
|
<body className={roboto.className}>
|
||||||
<ThemeProvider enableSystem={false} defaultTheme="light">
|
<ThemeProvider enableSystem={false} defaultTheme="dark">
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import ChatWindow from "@/widgets/chat-window";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -6,5 +7,9 @@ export const metadata: Metadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function Root() {
|
export default async function Root() {
|
||||||
return <></>;
|
return (
|
||||||
|
<main className="w-full h-full flex items-center justify-center p-8">
|
||||||
|
<ChatWindow />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
5
frontend/src/entities/message/index.ts
Normal file
5
frontend/src/entities/message/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { MessageService } from "./message.service";
|
||||||
|
|
||||||
|
export { type IMessage, messageSchema, messagesSchema } from "./message.schema";
|
||||||
|
|
||||||
|
export default MessageService;
|
||||||
11
frontend/src/entities/message/message.schema.ts
Normal file
11
frontend/src/entities/message/message.schema.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { object, string, number, InferType, array } from "yup";
|
||||||
|
|
||||||
|
export const messageSchema = object({
|
||||||
|
id: number().required().positive().integer(),
|
||||||
|
text: string().required(),
|
||||||
|
timeOfSend: string().required(),
|
||||||
|
sender: string().required(),
|
||||||
|
});
|
||||||
|
export const messagesSchema = array(messageSchema);
|
||||||
|
|
||||||
|
export type IMessage = InferType<typeof messageSchema>;
|
||||||
16
frontend/src/entities/message/message.service.ts
Normal file
16
frontend/src/entities/message/message.service.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import HTTPService from "@/shared/services/http";
|
||||||
|
import { messageSchema, messagesSchema } from "./message.schema";
|
||||||
|
|
||||||
|
export abstract class MessageService {
|
||||||
|
public static getTopOfHistory = () =>
|
||||||
|
HTTPService.get(`message`, messagesSchema);
|
||||||
|
|
||||||
|
public static sendMessage = (message: string, senderUUID: string) =>
|
||||||
|
HTTPService.post(`message`, messageSchema, {
|
||||||
|
body: {
|
||||||
|
text: message,
|
||||||
|
timeOfSend: new Date().toISOString(),
|
||||||
|
sender: senderUUID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
5
frontend/src/entities/user/index.ts
Normal file
5
frontend/src/entities/user/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { UserService } from "./user.service";
|
||||||
|
|
||||||
|
export { type IUser, userSchema } from "./user.schema";
|
||||||
|
|
||||||
|
export default UserService;
|
||||||
8
frontend/src/entities/user/user.schema.ts
Normal file
8
frontend/src/entities/user/user.schema.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { object, string, number, InferType } from "yup";
|
||||||
|
|
||||||
|
export const userSchema = object({
|
||||||
|
uuid: string().uuid().required(),
|
||||||
|
color: number().min(1).max(8).required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type IUser = InferType<typeof userSchema>;
|
||||||
25
frontend/src/entities/user/user.service.ts
Normal file
25
frontend/src/entities/user/user.service.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { v1 as uuidV1 } from "uuid";
|
||||||
|
import { IUser } from "./user.schema";
|
||||||
|
|
||||||
|
export abstract class UserService {
|
||||||
|
private static pickedColors: IUser["color"][] = [];
|
||||||
|
|
||||||
|
public static generateMe = (): IUser => ({
|
||||||
|
uuid: uuidV1(),
|
||||||
|
color: 8,
|
||||||
|
});
|
||||||
|
|
||||||
|
public static generateUser = (uuid: IUser["uuid"]): IUser => {
|
||||||
|
const newColor = Array.from(Array(7).keys())
|
||||||
|
.map((i) => i + 1)
|
||||||
|
.reduce(
|
||||||
|
(p, c) => (!p && !this.pickedColors.includes(c) ? c : p),
|
||||||
|
undefined as number | undefined
|
||||||
|
);
|
||||||
|
this.pickedColors.push(newColor ?? 1);
|
||||||
|
return {
|
||||||
|
uuid: uuid,
|
||||||
|
color: newColor ?? 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { SunIcon } from "@/shared/assets/icons";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
|
||||||
|
export const ColorSchemeSwitch = () => {
|
||||||
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SunIcon
|
||||||
|
className="h-10 min-w-10 max-w-10 cursor-pointer bg-bg3 p-1 rounded-lg hover:bg-bg4 transition"
|
||||||
|
onClick={() => setTheme(theme == "light" ? "dark" : "light")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
3
frontend/src/features/colorSchemeSwitch/index.ts
Normal file
3
frontend/src/features/colorSchemeSwitch/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { ColorSchemeSwitch } from "./colorSchemeSwitch";
|
||||||
|
|
||||||
|
export default ColorSchemeSwitch;
|
||||||
3
frontend/src/features/messagesList/index.ts
Normal file
3
frontend/src/features/messagesList/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { MessagesList } from "./messagesList";
|
||||||
|
|
||||||
|
export default MessagesList;
|
||||||
28
frontend/src/features/messagesList/message.tsx
Normal file
28
frontend/src/features/messagesList/message.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { IMessage } from "@/entities/message";
|
||||||
|
import { IUser } from "@/entities/user";
|
||||||
|
|
||||||
|
export const Message = ({
|
||||||
|
message,
|
||||||
|
color,
|
||||||
|
align = "right",
|
||||||
|
}: {
|
||||||
|
message: IMessage;
|
||||||
|
color: IUser["color"] | undefined;
|
||||||
|
align?: "left" | "right";
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={message.id}
|
||||||
|
style={{
|
||||||
|
background: `var(--color-col${color})`,
|
||||||
|
borderRadius: `var(--radius-xl)`,
|
||||||
|
[align === "right"
|
||||||
|
? "borderBottomRightRadius"
|
||||||
|
: "borderBottomLeftRadius"]: 0,
|
||||||
|
}}
|
||||||
|
className={`max-w-20 p-2`}
|
||||||
|
>
|
||||||
|
{message.text}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
73
frontend/src/features/messagesList/messagesList.tsx
Normal file
73
frontend/src/features/messagesList/messagesList.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import MessageService from "@/entities/message";
|
||||||
|
import UserService, { IUser } from "@/entities/user";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { Message } from "./message";
|
||||||
|
|
||||||
|
export const MessagesList = () => {
|
||||||
|
const { data: users, mutate: mutateUsers } = useSWR<IUser[]>("users");
|
||||||
|
const { data: messages, mutate: mutateMessages } = useSWR(
|
||||||
|
"messages",
|
||||||
|
MessageService.getTopOfHistory,
|
||||||
|
{ revalidateOnFocus: false }
|
||||||
|
);
|
||||||
|
const { data: me } = useSWR<IUser>("me");
|
||||||
|
const [websocket, updateWebSocket] = useState<WebSocket>();
|
||||||
|
const bottomRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const getUserColor = (userUUID: IUser["uuid"]) =>
|
||||||
|
users?.find((user) => user.uuid === userUUID)?.color;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
|
const newUsers: IUser[] = [];
|
||||||
|
messages?.forEach((message) => {
|
||||||
|
if (
|
||||||
|
!users?.concat(newUsers).find((user) => user.uuid === message.sender)
|
||||||
|
) {
|
||||||
|
const newUser = UserService.generateUser(message.sender);
|
||||||
|
newUsers.push(newUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mutateUsers([...(users ?? []), ...newUsers]);
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!websocket && messages && process.env.NEXT_PUBLIC_WS_URL) {
|
||||||
|
const websocket = new WebSocket(process.env.NEXT_PUBLIC_WS_URL);
|
||||||
|
websocket.onmessage = (event) => {
|
||||||
|
mutateMessages([...(messages ?? []), JSON.parse(event.data)], {
|
||||||
|
revalidate: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
websocket.onclose = function () {
|
||||||
|
setTimeout(() => updateWebSocket(undefined), 1000);
|
||||||
|
};
|
||||||
|
updateWebSocket(websocket);
|
||||||
|
}
|
||||||
|
return () => websocket?.close();
|
||||||
|
}, [websocket, messages]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-full h-full overflow-auto flex flex-col gap-2">
|
||||||
|
{messages?.map((message) => (
|
||||||
|
<div
|
||||||
|
key={message.id}
|
||||||
|
className="w-full flex"
|
||||||
|
style={{
|
||||||
|
justifyContent: message.sender === me?.uuid ? "end" : "start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Message
|
||||||
|
message={message}
|
||||||
|
color={getUserColor(message.sender)}
|
||||||
|
align={message.sender === me?.uuid ? "right" : "left"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div ref={bottomRef}></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
3
frontend/src/features/sendMessage/index.ts
Normal file
3
frontend/src/features/sendMessage/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { SendMessage } from "./sendMessage";
|
||||||
|
|
||||||
|
export default SendMessage;
|
||||||
41
frontend/src/features/sendMessage/sendMessage.tsx
Normal file
41
frontend/src/features/sendMessage/sendMessage.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import MessageService from "@/entities/message";
|
||||||
|
import UserService from "@/entities/user";
|
||||||
|
import { useState } from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
export const SendMessage = () => {
|
||||||
|
const [message, updateMessage] = useState<string>("");
|
||||||
|
const { data: me } = useSWR(`me`, UserService.generateMe);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full flex items-center gap-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={
|
||||||
|
"w-full h-full px-4 py-1 rounded-lg" +
|
||||||
|
" focus:outline-1 outline-fg4 focus:shadow-md"
|
||||||
|
}
|
||||||
|
placeholder="Напиши сообщение..."
|
||||||
|
value={message}
|
||||||
|
onChange={(e) => updateMessage(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
"h-full px-4 rounded-lg bg-ac2 disabled:bg-bg3" +
|
||||||
|
" cursor-pointer hover:shadow-md text-fg0 disabled:text-fg4"
|
||||||
|
}
|
||||||
|
disabled={!message || !me}
|
||||||
|
onClick={() => {
|
||||||
|
if (!!me) {
|
||||||
|
MessageService.sendMessage(message, me.uuid);
|
||||||
|
updateMessage("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Отправить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
2
frontend/src/shared/assets/icons/index.ts
Normal file
2
frontend/src/shared/assets/icons/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { SunIcon } from "./sunIcon";
|
||||||
|
export { SendIcon } from "./sendIcon";
|
||||||
33
frontend/src/shared/assets/icons/sendIcon.tsx
Normal file
33
frontend/src/shared/assets/icons/sendIcon.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export const SendIcon = ({
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className={className}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||||
|
<g
|
||||||
|
id="SVGRepo_tracerCarrier"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
></g>
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
{" "}
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M1.265 4.42619C1.04293 2.87167 2.6169 1.67931 4.05323 2.31397L21.8341 10.1706C23.423 10.8727 23.423 13.1273 21.8341 13.8294L4.05323 21.686C2.6169 22.3207 1.04293 21.1283 1.265 19.5738L1.99102 14.4917C2.06002 14.0087 2.41458 13.6156 2.88791 13.4972L8.87688 12L2.88791 10.5028C2.41458 10.3844 2.06002 9.99129 1.99102 9.50829L1.265 4.42619ZM21.0257 12L3.2449 4.14335L3.89484 8.69294L12.8545 10.9328C13.9654 11.2106 13.9654 12.7894 12.8545 13.0672L3.89484 15.3071L3.2449 19.8566L21.0257 12Z"
|
||||||
|
fill="var(--color-fg0)"
|
||||||
|
></path>{" "}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
31
frontend/src/shared/assets/icons/sunIcon.tsx
Normal file
31
frontend/src/shared/assets/icons/sunIcon.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export const SunIcon = ({
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="800px"
|
||||||
|
height="800px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className={className}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 3V4M12 20V21M4 12H3M6.31412 6.31412L5.5 5.5M17.6859
|
||||||
|
6.31412L18.5 5.5M6.31412 17.69L5.5 18.5001M17.6859
|
||||||
|
17.69L18.5 18.5001M21 12H20M16 12C16 14.2091 14.2091
|
||||||
|
16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086
|
||||||
|
8 12 8C14.2091 8 16 9.79086 16 12Z"
|
||||||
|
stroke="var(--color-fg1)"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
95
frontend/src/shared/services/http/http.ts
Normal file
95
frontend/src/shared/services/http/http.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { AnySchema } from "yup";
|
||||||
|
|
||||||
|
export type RequestCacheOptions = {
|
||||||
|
cache?: RequestCache;
|
||||||
|
next?: NextFetchRequestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GetRequestOptions = RequestCacheOptions & {
|
||||||
|
headers?: HeadersInit;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RequestOptions = GetRequestOptions & {
|
||||||
|
body?: BodyInit | object;
|
||||||
|
stringify?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export abstract class HTTPService {
|
||||||
|
private static deepUndefinedToNull(o?: object): object | undefined {
|
||||||
|
if (Array.isArray(o)) return o;
|
||||||
|
if (o)
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(o).map(([k, v]) => {
|
||||||
|
if (v === undefined) return [k, null];
|
||||||
|
if (typeof v === "object") return [k, this.deepUndefinedToNull(v)];
|
||||||
|
return [k, v];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async request<Y extends AnySchema>(
|
||||||
|
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||||
|
url: string,
|
||||||
|
schema: Y,
|
||||||
|
options?: RequestOptions
|
||||||
|
) {
|
||||||
|
return await fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_BASE_PROTOCOL}://` +
|
||||||
|
`${process.env.NEXT_PUBLIC_BASE_DOMAIN}:` +
|
||||||
|
`${process.env.NEXT_PUBLIC_BASE_PORT}` +
|
||||||
|
`${process.env.NEXT_PUBLIC_API_PATTERN}${url}`,
|
||||||
|
{
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
accept: "application/json",
|
||||||
|
...((options?.stringify ?? true) != true
|
||||||
|
? {}
|
||||||
|
: { "Content-Type": "application/json" }),
|
||||||
|
...options?.headers,
|
||||||
|
},
|
||||||
|
body:
|
||||||
|
(options?.stringify ?? true) != true
|
||||||
|
? (options?.body as BodyInit)
|
||||||
|
: JSON.stringify(
|
||||||
|
this.deepUndefinedToNull(options?.body as object | undefined)
|
||||||
|
),
|
||||||
|
cache: options?.cache ?? options?.next ? undefined : "no-cache",
|
||||||
|
next: options?.next ?? {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((r) => {
|
||||||
|
if (r && r.ok) return r;
|
||||||
|
else throw Error("Response ok = false");
|
||||||
|
})
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then(async (d) => await schema.validate(d))
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async get<Y extends AnySchema>(
|
||||||
|
url: string,
|
||||||
|
schema: Y,
|
||||||
|
options?: GetRequestOptions
|
||||||
|
) {
|
||||||
|
return await this.request<Y>("GET", url, schema, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async post<Y extends AnySchema>(
|
||||||
|
url: string,
|
||||||
|
schema: Y,
|
||||||
|
options?: RequestOptions
|
||||||
|
) {
|
||||||
|
return await this.request<Y>("POST", url, schema, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async put<Y extends AnySchema>(
|
||||||
|
url: string,
|
||||||
|
schema: Y,
|
||||||
|
options?: RequestOptions
|
||||||
|
) {
|
||||||
|
return await this.request<Y>("PUT", url, schema, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
frontend/src/shared/services/http/index.ts
Normal file
3
frontend/src/shared/services/http/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { HTTPService } from "./http";
|
||||||
|
|
||||||
|
export default HTTPService;
|
||||||
3
frontend/src/widgets/chat-window/index.ts
Normal file
3
frontend/src/widgets/chat-window/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { ChatWindow } from "./window";
|
||||||
|
|
||||||
|
export default ChatWindow;
|
||||||
25
frontend/src/widgets/chat-window/window.tsx
Normal file
25
frontend/src/widgets/chat-window/window.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import ColorSchemeSwitch from "@/features/colorSchemeSwitch";
|
||||||
|
import MessagesList from "@/features/messagesList";
|
||||||
|
import SendMessage from "@/features/sendMessage";
|
||||||
|
|
||||||
|
export const ChatWindow = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"w-lg h-full md:size-120 bg-bg1 p-4 rounded-lg " +
|
||||||
|
"drop-shadow-2xl flex flex-col gap-4 justify-between"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MessagesList />
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"w-full size-14 bg-bg2 p-2 rounded-lg drop-shadow-md " +
|
||||||
|
"flex flex-row gap-4 items-center justify-between"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ColorSchemeSwitch />
|
||||||
|
<SendMessage />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import type { Config } from "tailwindcss";
|
|
||||||
|
|
||||||
const config: Config = {
|
|
||||||
content: [
|
|
||||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
|
||||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
||||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
|
||||||
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
bg0: "var(--color-bg0)",
|
|
||||||
bg1: "var(--color-bg1)",
|
|
||||||
bg4: "var(--color-bg4)",
|
|
||||||
fg0: "var(--color-fg0)",
|
|
||||||
fg1: "var(--color-fg1)",
|
|
||||||
fg4: "var(--color-fg4)",
|
|
||||||
ac0: "var(--color-ac0)",
|
|
||||||
ac1: "var(--color-ac1)",
|
|
||||||
ac2: "var(--color-ac2)",
|
|
||||||
err: "var(--color-err)",
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
fadeIn: "fadeIn 0.25s ease-in-out",
|
|
||||||
fadeOut: "fadeOut 0.25s ease-in-out",
|
|
||||||
},
|
|
||||||
keyframes: () => ({
|
|
||||||
fadeIn: {
|
|
||||||
"0%": { opacity: "0" },
|
|
||||||
"100%": { opacity: "1" },
|
|
||||||
},
|
|
||||||
fadeOut: {
|
|
||||||
"0%": { opacity: "1" },
|
|
||||||
"100%": { opacity: "0" },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
screens: {
|
|
||||||
tb: "640px",
|
|
||||||
lp: "1024px",
|
|
||||||
dsk: "1280px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
};
|
|
||||||
export default config;
|
|
||||||
Reference in New Issue
Block a user