forked from sh-edraft.de/sh_discord_bot
		
	Repaired register by discord #70
This commit is contained in:
		| @@ -12,6 +12,12 @@ | |||||||
|     "FileLogLevel": "TRACE" |     "FileLogLevel": "TRACE" | ||||||
|   }, |   }, | ||||||
|   "BotLoggingSettings": { |   "BotLoggingSettings": { | ||||||
|  |     "Api": { | ||||||
|  |       "Path": "logs/", | ||||||
|  |       "Filename": "api.log", | ||||||
|  |       "ConsoleLogLevel": "TRACE", | ||||||
|  |       "FileLogLevel": "TRACE" | ||||||
|  |     }, | ||||||
|     "Command": { |     "Command": { | ||||||
|       "Path": "logs/", |       "Path": "logs/", | ||||||
|       "Filename": "commands.log", |       "Filename": "commands.log", | ||||||
| @@ -21,7 +27,7 @@ | |||||||
|     "Database": { |     "Database": { | ||||||
|       "Path": "logs/", |       "Path": "logs/", | ||||||
|       "Filename": "database.log", |       "Filename": "database.log", | ||||||
|       "ConsoleLogLevel": "DEBUG", |       "ConsoleLogLevel": "TRACE", | ||||||
|       "FileLogLevel": "TRACE" |       "FileLogLevel": "TRACE" | ||||||
|     }, |     }, | ||||||
|     "Message": { |     "Message": { | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ class AuthServiceABC(ABC): | |||||||
|     async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): pass |     async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): pass | ||||||
|  |  | ||||||
|     @abstractmethod |     @abstractmethod | ||||||
|     async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO) -> OAuthDTO: pass |     async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO, dc_id: int) -> OAuthDTO: pass | ||||||
|  |  | ||||||
|     @abstractmethod |     @abstractmethod | ||||||
|     async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass |     async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass | ||||||
|   | |||||||
| @@ -1 +1,28 @@ | |||||||
| {} | { | ||||||
|  |   "Api": { | ||||||
|  |     "Port": 5000, | ||||||
|  |     "Host": "0.0.0.0", | ||||||
|  |     "RedirectToHTTPS": false | ||||||
|  |   }, | ||||||
|  |   "Authentication": { | ||||||
|  |     "SecretKey": "RjNiNUxEeisjSnZ6Zz1XIUBnc2EleHNG", | ||||||
|  |     "Issuer": "http://localhost:5000", | ||||||
|  |     "Audience": "http://localhost:4200", | ||||||
|  |     "TokenExpireTime": 1, | ||||||
|  |     "RefreshTokenExpireTime": 7 | ||||||
|  |   }, | ||||||
|  |   "DiscordAuthentication": { | ||||||
|  |     "ClientSecret": "V3FTb3JYVFBiVktEeHZxdWJDWW4xcnBCbXRwdmpwcy0=", | ||||||
|  |     "_RedirectURL": "http://localhost:5000/api/auth/discord/register", | ||||||
|  |     "RedirectURL": "http://localhost:4200/auth/register", | ||||||
|  |     "Scope": [ | ||||||
|  |       "identify", | ||||||
|  |       "email" | ||||||
|  |     ], | ||||||
|  |     "TokenURL": "https://discordapp.com/api/oauth2/token", | ||||||
|  |     "AuthURL": "https://discordapp.com/api/oauth2/authorize" | ||||||
|  |   }, | ||||||
|  |   "Frontend": { | ||||||
|  |     "URL": "http://localhost:4200/" | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -78,9 +78,8 @@ class AuthDiscordController: | |||||||
|             response['email'], |             response['email'], | ||||||
|             str(uuid.uuid4()), |             str(uuid.uuid4()), | ||||||
|             None, |             None, | ||||||
|             AuthRoleEnum.normal, |             AuthRoleEnum.normal | ||||||
|             response['id'] |         ), response['id']) | ||||||
|         )) |  | ||||||
|         return jsonify(result.to_dict()) |         return jsonify(result.to_dict()) | ||||||
|  |  | ||||||
|     @Route.post(f'{BasePath}/register') |     @Route.post(f'{BasePath}/register') | ||||||
|   | |||||||
| @@ -8,8 +8,9 @@ from typing import Optional | |||||||
| import jwt | import jwt | ||||||
| from cpl_core.database.context import DatabaseContextABC | from cpl_core.database.context import DatabaseContextABC | ||||||
| from cpl_core.environment import ApplicationEnvironmentABC | from cpl_core.environment import ApplicationEnvironmentABC | ||||||
| from cpl_core.mailing import EMailClientABC, EMail | from cpl_core.mailing import EMail | ||||||
| from cpl_core.utils import CredentialManager | from cpl_core.utils import CredentialManager | ||||||
|  | from cpl_discord.service import DiscordBotServiceABC | ||||||
| from cpl_query.extension import List | from cpl_query.extension import List | ||||||
| from cpl_translation import TranslatePipe | from cpl_translation import TranslatePipe | ||||||
| from flask import request | from flask import request | ||||||
| @@ -31,9 +32,12 @@ from bot_api.model.token_dto import TokenDTO | |||||||
| from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO | from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO | ||||||
| from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT | from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT | ||||||
| from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | ||||||
|  | from bot_data.abc.server_repository_abc import ServerRepositoryABC | ||||||
| from bot_data.abc.user_repository_abc import UserRepositoryABC | from bot_data.abc.user_repository_abc import UserRepositoryABC | ||||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | from bot_data.model.auth_role_enum import AuthRoleEnum | ||||||
| from bot_data.model.auth_user import AuthUser | from bot_data.model.auth_user import AuthUser | ||||||
|  | from bot_data.model.auth_user_users_relation import AuthUserUsersRelation | ||||||
|  | from bot_data.model.user import User | ||||||
|  |  | ||||||
| _email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' | _email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' | ||||||
|  |  | ||||||
| @@ -44,9 +48,11 @@ class AuthService(AuthServiceABC): | |||||||
|             self, |             self, | ||||||
|             env: ApplicationEnvironmentABC, |             env: ApplicationEnvironmentABC, | ||||||
|             logger: ApiLogger, |             logger: ApiLogger, | ||||||
|  |             bot: DiscordBotServiceABC, | ||||||
|  |             db: DatabaseContextABC, | ||||||
|             auth_users: AuthUserRepositoryABC, |             auth_users: AuthUserRepositoryABC, | ||||||
|             users: UserRepositoryABC, |             users: UserRepositoryABC, | ||||||
|             db: DatabaseContextABC, |             servers: ServerRepositoryABC, | ||||||
|             mailer: MailThread, |             mailer: MailThread, | ||||||
|             t: TranslatePipe, |             t: TranslatePipe, | ||||||
|             auth_settings: AuthenticationSettings, |             auth_settings: AuthenticationSettings, | ||||||
| @@ -57,9 +63,11 @@ class AuthService(AuthServiceABC): | |||||||
|  |  | ||||||
|         self._environment = env |         self._environment = env | ||||||
|         self._logger = logger |         self._logger = logger | ||||||
|  |         self._bot = bot | ||||||
|  |         self._db = db | ||||||
|         self._auth_users = auth_users |         self._auth_users = auth_users | ||||||
|         self._users = users |         self._users = users | ||||||
|         self._db = db |         self._servers = servers | ||||||
|         self._mailer = mailer |         self._mailer = mailer | ||||||
|         self._t = t |         self._t = t | ||||||
|         self._auth_settings = auth_settings |         self._auth_settings = auth_settings | ||||||
| @@ -134,7 +142,7 @@ class AuthService(AuthServiceABC): | |||||||
|     def _create_and_save_refresh_token(self, user: AuthUser) -> str: |     def _create_and_save_refresh_token(self, user: AuthUser) -> str: | ||||||
|         token = str(uuid.uuid4()) |         token = str(uuid.uuid4()) | ||||||
|         user.refresh_token = token |         user.refresh_token = token | ||||||
|         user.refresh_token_expire_time = datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.refresh_token_expire_time) |         user.refresh_token_expire_time = datetime.now() + timedelta(days=self._auth_settings.refresh_token_expire_time) | ||||||
|         self._auth_users.update_auth_user(user) |         self._auth_users.update_auth_user(user) | ||||||
|         self._db.save_changes() |         self._db.save_changes() | ||||||
|         return token |         return token | ||||||
| @@ -244,20 +252,21 @@ class AuthService(AuthServiceABC): | |||||||
|         self._auth_users.update_auth_user(db_user) |         self._auth_users.update_auth_user(db_user) | ||||||
|         self._db.save_changes() |         self._db.save_changes() | ||||||
|  |  | ||||||
|     async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO) -> OAuthDTO: |     async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO, dc_id: int) -> OAuthDTO: | ||||||
|         db_user = self._auth_users.find_auth_user_by_email(user_dto.email) |         db_auth_user = self._auth_users.find_auth_user_by_email(user_dto.email) | ||||||
|  |  | ||||||
|         # user exists |         # user exists | ||||||
|         if db_user is not None and db_user.user_id is not None: |         if db_auth_user is not None and db_auth_user.users.count() > 0: | ||||||
|             # raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') |             # raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') | ||||||
|             self._logger.debug(__name__, f'Discord user already exists') |             self._logger.debug(__name__, f'Discord user already exists') | ||||||
|             return OAuthDTO(AUT.to_dto(db_user), None) |             return OAuthDTO(AUT.to_dto(db_auth_user), None) | ||||||
|  |  | ||||||
|         # user exists but discord user id not set |         # user exists but discord user id not set | ||||||
|         elif db_user is not None and db_user.user_id is None: |         elif db_auth_user is not None and db_auth_user.users.count() == 0: | ||||||
|             self._logger.debug(__name__, f'Auth user exists but not linked with discord') |             self._logger.debug(__name__, f'Auth user exists but not linked with discord') | ||||||
|             user = self._users.get_users_by_discord_id(user_dto.user_id).single() |             # users = self._users.get_users_by_discord_id(user_dto.user_id) | ||||||
|             db_user.user_id = user.user_id |             # add auth_user to user refs | ||||||
|             db_user.oauth_id = None |             db_auth_user.oauth_id = None | ||||||
|  |  | ||||||
|         else: |         else: | ||||||
|             # user does not exists |             # user does not exists | ||||||
| @@ -269,12 +278,24 @@ class AuthService(AuthServiceABC): | |||||||
|                 user_dto.user_id = None |                 user_dto.user_id = None | ||||||
|  |  | ||||||
|             await self.add_auth_user_async(user_dto) |             await self.add_auth_user_async(user_dto) | ||||||
|             db_user = self._auth_users.get_auth_user_by_email(user_dto.email) |             db_auth_user = self._auth_users.get_auth_user_by_email(user_dto.email) | ||||||
|             db_user.oauth_id = uuid.uuid4() |             db_auth_user.oauth_id = uuid.uuid4() | ||||||
|  |  | ||||||
|         self._auth_users.update_auth_user(db_user) |         for g in self._bot.guilds: | ||||||
|  |             member = g.get_member(int(dc_id)) | ||||||
|  |             if member is None: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             server = self._servers.get_server_by_discord_id(g.id) | ||||||
|  |             users = self._users.get_users_by_discord_id(dc_id) | ||||||
|  |             for user in users: | ||||||
|  |                 if user.server.server_id != server.server_id: | ||||||
|  |                     continue | ||||||
|  |                 self._auth_users.add_auth_user_user_rel(AuthUserUsersRelation(db_auth_user, user)) | ||||||
|  |  | ||||||
|  |         self._auth_users.update_auth_user(db_auth_user) | ||||||
|         self._db.save_changes() |         self._db.save_changes() | ||||||
|         return OAuthDTO(AUT.to_dto(db_user), db_user.oauth_id) |         return OAuthDTO(AUT.to_dto(db_auth_user), db_auth_user.oauth_id) | ||||||
|  |  | ||||||
|     async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): |     async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): | ||||||
|         if update_user_dto is None: |         if update_user_dto is None: | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ class AuthUserTransformer(TransformerABC): | |||||||
|             None, |             None, | ||||||
|             None, |             None, | ||||||
|             None, |             None, | ||||||
|             datetime.now(tz=timezone.utc), |             datetime.now(), | ||||||
|             AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), |             AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), | ||||||
|             dto.user_id, |             dto.user_id, | ||||||
|             auth_user_id=0 if dto.id is None else dto.id |             auth_user_id=0 if dto.id is None else dto.id | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ from cpl_query.extension import List | |||||||
| from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria | from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria | ||||||
| from bot_data.filtered_result import FilteredResult | from bot_data.filtered_result import FilteredResult | ||||||
| from bot_data.model.auth_user import AuthUser | from bot_data.model.auth_user import AuthUser | ||||||
|  | from bot_data.model.auth_user_users_relation import AuthUserUsersRelation | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthUserRepositoryABC(ABC): | class AuthUserRepositoryABC(ABC): | ||||||
| @@ -39,3 +40,12 @@ class AuthUserRepositoryABC(ABC): | |||||||
|  |  | ||||||
|     @abstractmethod |     @abstractmethod | ||||||
|     def delete_auth_user(self, user: AuthUser): pass |     def delete_auth_user(self, user: AuthUser): pass | ||||||
|  |  | ||||||
|  |     @abstractmethod | ||||||
|  |     def add_auth_user_user_rel(self, rel: AuthUserUsersRelation): pass | ||||||
|  |  | ||||||
|  |     @abstractmethod | ||||||
|  |     def update_auth_user_user_rel(self, rel: AuthUserUsersRelation): pass | ||||||
|  |  | ||||||
|  |     @abstractmethod | ||||||
|  |     def delete_auth_user_user_rel(self, rel: AuthUserUsersRelation): pass | ||||||
|   | |||||||
| @@ -30,8 +30,8 @@ class ApiMigration(MigrationABC): | |||||||
|                 `OAuthId` VARCHAR(255) DEFAULT NULL, |                 `OAuthId` VARCHAR(255) DEFAULT NULL, | ||||||
|                 `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, |                 `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, | ||||||
|                 `AuthRole` INT NOT NULL DEFAULT '0', |                 `AuthRole` INT NOT NULL DEFAULT '0', | ||||||
|                 `CreatedOn` DATETIME(6) NOT NULL, |                 `CreatedAt` DATETIME(6) NOT NULL, | ||||||
|                 `LastModifiedOn` DATETIME(6) NOT NULL, |                 `LastModifiedAt` DATETIME(6) NOT NULL, | ||||||
|                 PRIMARY KEY(`Id`) |                 PRIMARY KEY(`Id`) | ||||||
|             ); |             ); | ||||||
|             """) |             """) | ||||||
| @@ -43,8 +43,8 @@ class ApiMigration(MigrationABC): | |||||||
|                 `Id` BIGINT NOT NULL AUTO_INCREMENT, |                 `Id` BIGINT NOT NULL AUTO_INCREMENT, | ||||||
|                 `AuthUserId` BIGINT DEFAULT NULL, |                 `AuthUserId` BIGINT DEFAULT NULL, | ||||||
|                 `UserId` BIGINT DEFAULT NULL, |                 `UserId` BIGINT DEFAULT NULL, | ||||||
|                 `CreatedOn` DATETIME(6) NOT NULL, |                 `CreatedAt` DATETIME(6) NOT NULL, | ||||||
|                 `LastModifiedOn` DATETIME(6) NOT NULL, |                 `LastModifiedAt` DATETIME(6) NOT NULL, | ||||||
|                 PRIMARY KEY(`Id`), |                 PRIMARY KEY(`Id`), | ||||||
|                 FOREIGN KEY (`AuthUserId`) REFERENCES `AuthUsers`(`Id`), |                 FOREIGN KEY (`AuthUserId`) REFERENCES `AuthUsers`(`Id`), | ||||||
|                 FOREIGN KEY (`UserId`) REFERENCES `Users`(`UserId`) |                 FOREIGN KEY (`UserId`) REFERENCES `Users`(`UserId`) | ||||||
|   | |||||||
| @@ -207,8 +207,8 @@ class AuthUser(TableABC): | |||||||
|                 `OAuthId`, |                 `OAuthId`, | ||||||
|                 `RefreshTokenExpiryTime`, |                 `RefreshTokenExpiryTime`, | ||||||
|                 `AuthRole`, |                 `AuthRole`, | ||||||
|                 `CreatedOn`, |                 `CreatedAt`, | ||||||
|                 `LastModifiedOn` |                 `LastModifiedAt` | ||||||
|                 ) VALUES ( |                 ) VALUES ( | ||||||
|                     {self._auth_user_id},  |                     {self._auth_user_id},  | ||||||
|                     '{self._first_name}', |                     '{self._first_name}', | ||||||
| @@ -220,7 +220,7 @@ class AuthUser(TableABC): | |||||||
|                     '{"NULL" if self._confirmation_id is None else self._confirmation_id}', |                     '{"NULL" if self._confirmation_id is None else self._confirmation_id}', | ||||||
|                     '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', |                     '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', | ||||||
|                     '{"NULL" if self._oauth_id is None else self._oauth_id}', |                     '{"NULL" if self._oauth_id is None else self._oauth_id}', | ||||||
|                     '{self._refresh_token_expire_time}', |                     '{self._refresh_token_expire_time.isoformat()}', | ||||||
|                     {self._auth_role_id.value}, |                     {self._auth_role_id.value}, | ||||||
|                     '{self._created_at}',  |                     '{self._created_at}',  | ||||||
|                     '{self._modified_at}' |                     '{self._modified_at}' | ||||||
| @@ -240,9 +240,9 @@ class AuthUser(TableABC): | |||||||
|             `ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}',  |             `ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}',  | ||||||
|             `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', |             `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', | ||||||
|             `OAuthId` = '{"NULL" if self._oauth_id is None else self._oauth_id}', |             `OAuthId` = '{"NULL" if self._oauth_id is None else self._oauth_id}', | ||||||
|             `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time}', |             `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time.isoformat()}', | ||||||
|             `AuthRole` = {self._auth_role_id.value}, |             `AuthRole` = {self._auth_role_id.value}, | ||||||
|             `LastModifiedOn` = '{self._modified_at}' |             `LastModifiedAt` = '{self._modified_at}' | ||||||
|             WHERE `AuthUsers`.`Id` = {self._auth_user_id}; |             WHERE `AuthUsers`.`Id` = {self._auth_user_id}; | ||||||
|         """) |         """) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,9 @@ | |||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from typing import Optional |  | ||||||
|  |  | ||||||
| from cpl_core.database import TableABC | from cpl_core.database import TableABC | ||||||
|  |  | ||||||
| from bot_data.model.auth_user import AuthUser | from bot_data.model.auth_user import AuthUser | ||||||
| from bot_data.model.user import User | from bot_data.model.user import User | ||||||
| from bot_data.model.server import Server |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthUserUsersRelation(TableABC): | class AuthUserUsersRelation(TableABC): | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ from bot_data.abc.user_repository_abc import UserRepositoryABC | |||||||
| from bot_data.filtered_result import FilteredResult | from bot_data.filtered_result import FilteredResult | ||||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | from bot_data.model.auth_role_enum import AuthRoleEnum | ||||||
| from bot_data.model.auth_user import AuthUser | from bot_data.model.auth_user import AuthUser | ||||||
|  | from bot_data.model.auth_user_users_relation import AuthUserUsersRelation | ||||||
| from bot_data.model.user import User | from bot_data.model.user import User | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -145,3 +146,15 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): | |||||||
|     def delete_auth_user(self, user: AuthUser): |     def delete_auth_user(self, user: AuthUser): | ||||||
|         self._logger.trace(__name__, f'Send SQL command: {user.delete_string}') |         self._logger.trace(__name__, f'Send SQL command: {user.delete_string}') | ||||||
|         self._context.cursor.execute(user.delete_string) |         self._context.cursor.execute(user.delete_string) | ||||||
|  |  | ||||||
|  |     def add_auth_user_user_rel(self, rel: AuthUserUsersRelation): | ||||||
|  |         self._logger.trace(__name__, f'Send SQL command: {rel.insert_string}') | ||||||
|  |         self._context.cursor.execute(rel.insert_string) | ||||||
|  |  | ||||||
|  |     def update_auth_user_user_rel(self, rel: AuthUserUsersRelation): | ||||||
|  |         self._logger.trace(__name__, f'Send SQL command: {rel.udpate_string}') | ||||||
|  |         self._context.cursor.execute(rel.udpate_string) | ||||||
|  |  | ||||||
|  |     def delete_auth_user_user_rel(self, rel: AuthUserUsersRelation): | ||||||
|  |         self._logger.trace(__name__, f'Send SQL command: {rel.delete_string}') | ||||||
|  |         self._context.cursor.execute(rel.delete_string) | ||||||
|   | |||||||
| @@ -101,5 +101,8 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   }, | ||||||
|  |   "cli": { | ||||||
|  |     "analytics": false | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										42898
									
								
								kdb-web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										42898
									
								
								kdb-web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -49,4 +49,4 @@ | |||||||
|         "ts-node": "~8.3.0", |         "ts-node": "~8.3.0", | ||||||
|         "typescript": "~4.7.2" |         "typescript": "~4.7.2" | ||||||
|     } |     } | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user