Add basic auth

This commit is contained in:
Денис 2026-02-17 16:24:30 +03:00
parent 2a01bf1584
commit 307d7a9104
9 changed files with 164 additions and 308 deletions

View File

@ -29,9 +29,6 @@ importers:
'@nestjs/schedule':
specifier: ^6.0.0
version: 6.1.0(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)
'@nestjs/serve-static':
specifier: ^4.0.2
version: 4.0.2(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)(express@4.21.2)
'@nestjs/typeorm':
specifier: ^11.0.0
version: 11.0.0(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(pg@8.16.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.5.4)))
@ -735,22 +732,6 @@ packages:
peerDependencies:
typescript: '>=4.8.2'
'@nestjs/serve-static@4.0.2':
resolution: {integrity: sha512-cT0vdWN5ar7jDI2NKbhf4LcwJzU4vS5sVpMkVrHuyLcltbrz6JdGi1TfIMMatP2pNiq5Ie/uUdPSFDVaZX/URQ==}
peerDependencies:
'@fastify/static': ^6.5.0 || ^7.0.0
'@nestjs/common': ^9.0.0 || ^10.0.0
'@nestjs/core': ^9.0.0 || ^10.0.0
express: ^4.18.1
fastify: ^4.7.0
peerDependenciesMeta:
'@fastify/static':
optional: true
express:
optional: true
fastify:
optional: true
'@nestjs/testing@11.1.13':
resolution: {integrity: sha512-bOWP8nLEZAOEEX8jAZGBCc1yU0+nv4g2ipc+QEzkVUe3eEEUKHKaeGafJ3GtDuGavlZKfkXEqflZuICdavu5dQ==}
peerDependencies:
@ -1125,10 +1106,6 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
accepts@2.0.0:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
@ -1249,9 +1226,6 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
array-flatten@1.1.1:
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
array-timsort@1.0.3:
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
@ -1315,10 +1289,6 @@ packages:
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
body-parser@2.2.2:
resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
engines: {node: '>=18'}
@ -1514,10 +1484,6 @@ packages:
console-control-strings@1.1.0:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
content-disposition@1.0.1:
resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
engines: {node: '>=18'}
@ -1529,9 +1495,6 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
cookie-signature@1.2.2:
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
engines: {node: '>=6.6.0'}
@ -1578,14 +1541,6 @@ packages:
dayjs@1.11.19:
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@ -1628,10 +1583,6 @@ packages:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
@ -1697,10 +1648,6 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
@ -1841,10 +1788,6 @@ packages:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
express@5.2.1:
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
engines: {node: '>= 18'}
@ -1898,10 +1841,6 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
engines: {node: '>= 0.8'}
finalhandler@2.1.1:
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
engines: {node: '>= 18.0.0'}
@ -1947,10 +1886,6 @@ packages:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
fresh@0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}
fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
@ -2105,10 +2040,6 @@ packages:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
iconv-lite@0.7.2:
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
engines: {node: '>=0.10.0'}
@ -2549,9 +2480,6 @@ packages:
resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
engines: {node: '>= 4.0.0'}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
merge-descriptors@2.0.0:
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
engines: {node: '>=18'}
@ -2587,11 +2515,6 @@ packages:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
hasBin: true
mime@2.6.0:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
engines: {node: '>=4.0.0'}
@ -2644,9 +2567,6 @@ packages:
engines: {node: '>=10'}
hasBin: true
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -2666,10 +2586,6 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
negotiator@1.0.0:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
@ -2810,12 +2726,6 @@ packages:
resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==}
engines: {node: 20 || >=22}
path-to-regexp@0.1.12:
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
path-to-regexp@0.2.5:
resolution: {integrity: sha512-l6qtdDPIkmAmzEO6egquYDfqQGPMRNGjYtrU13HAXb3YSRrt7HSb1sJY0pKp6o2bAa86tSB6iwaW2JbthPKr7Q==}
path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
@ -2939,10 +2849,6 @@ packages:
pure-rand@6.1.0:
resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
@ -2961,10 +2867,6 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
raw-body@3.0.2:
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'}
@ -3067,10 +2969,6 @@ packages:
engines: {node: '>=10'}
hasBin: true
send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
send@1.2.1:
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
engines: {node: '>= 18'}
@ -3078,10 +2976,6 @@ packages:
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
serve-static@1.16.2:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
serve-static@2.2.1:
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
engines: {node: '>= 18'}
@ -3536,10 +3430,6 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
uuid@11.1.0:
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
hasBin: true
@ -4444,14 +4334,6 @@ snapshots:
transitivePeerDependencies:
- chokidar
'@nestjs/serve-static@4.0.2(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)(express@4.21.2)':
dependencies:
'@nestjs/common': 11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core': 11.1.13(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)
path-to-regexp: 0.2.5
optionalDependencies:
express: 4.21.2
'@nestjs/testing@11.1.13(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)(@nestjs/platform-express@11.1.13)':
dependencies:
'@nestjs/common': 11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@ -4929,12 +4811,6 @@ snapshots:
dependencies:
event-target-shim: 5.0.1
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
negotiator: 0.6.3
optional: true
accepts@2.0.0:
dependencies:
mime-types: 3.0.2
@ -5035,9 +4911,6 @@ snapshots:
argparse@2.0.1: {}
array-flatten@1.1.1:
optional: true
array-timsort@1.0.3: {}
array-union@2.1.0: {}
@ -5129,24 +5002,6 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
body-parser@1.20.3:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
raw-body: 2.5.2
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
optional: true
body-parser@2.2.2:
dependencies:
bytes: 3.1.2
@ -5340,20 +5195,12 @@ snapshots:
console-control-strings@1.1.0: {}
content-disposition@0.5.4:
dependencies:
safe-buffer: 5.2.1
optional: true
content-disposition@1.0.1: {}
content-type@1.0.5: {}
convert-source-map@2.0.0: {}
cookie-signature@1.0.6:
optional: true
cookie-signature@1.2.2: {}
cookie@0.7.1: {}
@ -5406,11 +5253,6 @@ snapshots:
dayjs@1.11.19: {}
debug@2.6.9:
dependencies:
ms: 2.0.0
optional: true
debug@4.4.3:
dependencies:
ms: 2.1.3
@ -5437,9 +5279,6 @@ snapshots:
depd@2.0.0: {}
destroy@1.2.0:
optional: true
detect-libc@2.1.2: {}
detect-newline@3.1.0: {}
@ -5489,9 +5328,6 @@ snapshots:
emoji-regex@9.2.2: {}
encodeurl@1.0.2:
optional: true
encodeurl@2.0.0: {}
enhanced-resolve@5.18.4:
@ -5649,43 +5485,6 @@ snapshots:
jest-message-util: 29.7.0
jest-util: 29.7.0
express@4.21.2:
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.20.3
content-disposition: 0.5.4
content-type: 1.0.5
cookie: 0.7.1
cookie-signature: 1.0.6
debug: 2.6.9
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.3.1
fresh: 0.5.2
http-errors: 2.0.0
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.12
proxy-addr: 2.0.7
qs: 6.13.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
serve-static: 1.16.2
setprototypeof: 1.2.0
statuses: 2.0.1
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
transitivePeerDependencies:
- supports-color
optional: true
express@5.2.1:
dependencies:
accepts: 2.0.0
@ -5768,19 +5567,6 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
finalhandler@1.3.1:
dependencies:
debug: 2.6.9
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
optional: true
finalhandler@2.1.1:
dependencies:
debug: 4.4.3
@ -5853,9 +5639,6 @@ snapshots:
forwarded@0.2.0: {}
fresh@0.5.2:
optional: true
fresh@2.0.0: {}
fs-extra@10.1.0:
@ -6036,11 +5819,6 @@ snapshots:
human-signals@2.1.0: {}
iconv-lite@0.4.24:
dependencies:
safer-buffer: 2.1.2
optional: true
iconv-lite@0.7.2:
dependencies:
safer-buffer: 2.1.2
@ -6629,9 +6407,6 @@ snapshots:
dependencies:
fs-monkey: 1.1.0
merge-descriptors@1.0.3:
optional: true
merge-descriptors@2.0.0: {}
merge-stream@2.0.0: {}
@ -6657,9 +6432,6 @@ snapshots:
dependencies:
mime-db: 1.54.0
mime@1.6.0:
optional: true
mime@2.6.0: {}
mimic-fn@2.1.0: {}
@ -6701,9 +6473,6 @@ snapshots:
mkdirp@1.0.4: {}
ms@2.0.0:
optional: true
ms@2.1.3: {}
multer@1.4.5-lts.2:
@ -6730,9 +6499,6 @@ snapshots:
natural-compare@1.4.0: {}
negotiator@0.6.3:
optional: true
negotiator@1.0.0: {}
neo-async@2.6.2: {}
@ -6864,11 +6630,6 @@ snapshots:
lru-cache: 11.2.6
minipass: 7.1.2
path-to-regexp@0.1.12:
optional: true
path-to-regexp@0.2.5: {}
path-to-regexp@8.3.0: {}
path-type@4.0.0: {}
@ -6966,11 +6727,6 @@ snapshots:
pure-rand@6.1.0: {}
qs@6.13.0:
dependencies:
side-channel: 1.1.0
optional: true
qs@6.14.0:
dependencies:
side-channel: 1.1.0
@ -6987,14 +6743,6 @@ snapshots:
range-parser@1.2.1: {}
raw-body@2.5.2:
dependencies:
bytes: 3.1.2
http-errors: 2.0.0
iconv-lite: 0.4.24
unpipe: 1.0.0
optional: true
raw-body@3.0.2:
dependencies:
bytes: 3.1.2
@ -7100,25 +6848,6 @@ snapshots:
semver@7.7.3: {}
send@0.19.0:
dependencies:
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 2.0.0
mime: 1.6.0
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
optional: true
send@1.2.1:
dependencies:
debug: 4.4.3
@ -7139,16 +6868,6 @@ snapshots:
dependencies:
randombytes: 2.1.0
serve-static@1.16.2:
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.19.0
transitivePeerDependencies:
- supports-color
optional: true
serve-static@2.2.1:
dependencies:
encodeurl: 2.0.0
@ -7564,9 +7283,6 @@ snapshots:
util-deprecate@1.0.2: {}
utils-merge@1.0.1:
optional: true
uuid@11.1.0: {}
v8-compile-cache-lib@3.0.1: {}

View File

@ -9,6 +9,7 @@ import { BotModule } from './bot/bot.module';
import { CommonModule } from './common/common.module';
import { validate } from './config/env/validate';
import { DatabaseModule } from './database/database.module';
import { UsersModule } from './users/users.module';
@Module({
imports: [
@ -18,6 +19,7 @@ import { DatabaseModule } from './database/database.module';
AdminsModule,
AuthModule,
AdminConsoleModule,
UsersModule,
BotModule,
],
controllers: [AppController],

View File

@ -2,11 +2,13 @@ import { Module } from '@nestjs/common';
import { BotService } from './bot.service';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
ConfigModule,
ScheduleModule.forRoot(),
UsersModule,
],
providers: [BotService],
exports: [BotService],

View File

@ -1,7 +1,9 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Bot, Context, Keyboard } from 'grammy';
import { Bot, Keyboard } from 'grammy';
import { ForceReply, InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove } from 'grammy/types';
import { UsersService } from '../users/users.service';
import { BotContext } from './types/bot-context.types';
@Injectable()
export class BotService {
@ -10,26 +12,59 @@ export class BotService {
constructor(
private readonly configService: ConfigService,
private readonly usersService: UsersService,
) {
const telegramBotToken = this.configService.get<string>('TELEGRAM_BOT_TOKEN');
this.bot = new Bot(telegramBotToken);
}
public onModuleInit() {
this.bot.use(this.checkUserAuth.bind(this));
this.bot.command('start', this.onStart.bind(this));
this.bot.start().catch((error) => console.error(':: BOT ERROR:', error));
this._logger.log('BOT STARTED!');
}
private async onStart(ctx: Context) {
private async checkUserAuth(ctx: BotContext, next: () => Promise<void>) {
if (!ctx.from) {
return next();
}
try {
const user = await this.usersService.findOrCreate({
id: ctx.from.id,
username: ctx.from.username,
first_name: ctx.from.first_name,
last_name: ctx.from.last_name,
language_code: ctx.from.language_code,
});
if (!user.is_active) {
await ctx.reply('❌ Your account has been deactivated. Please contact support.');
return;
}
ctx.user = user;
return next();
} catch (error) {
this._logger.error(`Error in checkUserAuth: ${error.message}`, error.stack);
return next();
}
}
private async onStart(ctx: BotContext) {
try {
const name = ctx.user?.first_name || 'there';
const keyboard = new Keyboard()
.text('Menu')
.row()
.resized();
await ctx.reply('Welcome! This bot is under development.', {
await ctx.reply(`Welcome, ${name}! 👋\n\nThis bot is under development.`, {
reply_markup: keyboard,
});
} catch (error) {
@ -37,27 +72,6 @@ export class BotService {
}
}
/**
* Send a message to a specific Telegram user.
*
* @param telegramId - Telegram user chat ID (as string)
* @param message - Message text (supports Markdown)
* @param reply_markup - Optional keyboard (inline, reply, or remove)
* @throws Logs error if message sending fails
*
* @example
* ```typescript
* // Simple message
* await botService.sendMessage('123456789', 'Hello!');
*
* // With inline keyboard
* const keyboard = new InlineKeyboard()
* .text('Button 1', 'data1')
* .row()
* .text('Button 2', 'data2');
* await botService.sendMessage('123456789', 'Choose:', keyboard);
* ```
*/
public async sendMessage(
telegramId: string,
message: string,

View File

@ -0,0 +1,6 @@
import { Context } from 'grammy';
import { User } from '../../users/entities/user.entity';
export interface BotContext extends Context {
user?: User;
}

View File

@ -3,6 +3,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Password } from 'src/admins/entities/password.entity';
import { Admin } from 'src/admins/entities/admin.entity';
import { User } from 'src/users/entities/user.entity';
@Module({
imports: [
@ -22,6 +23,7 @@ import { Admin } from 'src/admins/entities/admin.entity';
entities: [
Admin,
Password,
User,
]
}),
}),

View File

@ -0,0 +1,24 @@
import { AbstractEntity } from 'src/common/entities/abstract.entity';
import { Column, Entity, Index } from 'typeorm';
@Entity('users')
@Index(['telegram_id'], { unique: true })
export class User extends AbstractEntity {
@Column({ type: 'bigint', unique: true })
telegram_id: string;
@Column({ length: 255, nullable: true, default: null })
username: string;
@Column({ length: 255 })
first_name: string;
@Column({ length: 255, nullable: true, default: null })
last_name: string;
@Column({ length: 10, nullable: true, default: null })
language_code: string;
@Column({ default: true })
is_active: boolean;
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}

View File

@ -0,0 +1,79 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
@Injectable()
export class UsersService {
private readonly _logger = new Logger(UsersService.name);
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
async findOrCreate(telegramUser: {
id: number;
username?: string;
first_name: string;
last_name?: string;
language_code?: string;
}): Promise<User> {
const telegramId = telegramUser.id.toString();
let user = await this.userRepository.findOne({
where: { telegram_id: telegramId },
});
if (user) {
user.username = telegramUser.username || null;
user.first_name = telegramUser.first_name;
user.last_name = telegramUser.last_name || null;
user.language_code = telegramUser.language_code || null;
user = await this.userRepository.save(user);
this._logger.debug(`Updated user: ${telegramId}`);
} else {
user = this.userRepository.create({
telegram_id: telegramId,
username: telegramUser.username || null,
first_name: telegramUser.first_name,
last_name: telegramUser.last_name || null,
language_code: telegramUser.language_code || null,
is_active: true,
});
user = await this.userRepository.save(user);
this._logger.log(`Created new user: ${telegramId} (${telegramUser.first_name})`);
}
return user;
}
async findByTelegramId(telegramId: string): Promise<User | null> {
return this.userRepository.findOne({
where: { telegram_id: telegramId },
});
}
async findOneByTelegramId(telegramId: string): Promise<User> {
const user = await this.findByTelegramId(telegramId);
if (!user) {
throw new Error(`User with telegram_id ${telegramId} not found`);
}
return user;
}
async isUserActive(telegramId: string): Promise<boolean> {
const user = await this.findByTelegramId(telegramId);
return user ? user.is_active : false;
}
async updateActiveStatus(telegramId: string, isActive: boolean): Promise<User> {
const user = await this.findOneByTelegramId(telegramId);
user.is_active = isActive;
return this.userRepository.save(user);
}
}