diff --git a/.env.example b/.env.example index 8f4ccd2..a61b666 100644 --- a/.env.example +++ b/.env.example @@ -4,4 +4,6 @@ DATABASE_USER=postgres DATABASE_HOST=localhost DATABASE_PORT=5432 -SERVER_PORT=3000 \ No newline at end of file +SERVER_PORT=3000 + +ACCESS_TOKEN=123 \ No newline at end of file diff --git a/backend/config/index.ts b/backend/config/index.ts index c359859..187737f 100644 --- a/backend/config/index.ts +++ b/backend/config/index.ts @@ -16,5 +16,6 @@ export const config = { }, server: { port: +process.env.SERVER_PORT || 8080, + access_token: process.env.ACCESS_TOKEN || '', }, }; diff --git a/backend/libs/database/settings.entity.ts b/backend/libs/database/settings.entity.ts new file mode 100644 index 0000000..f193388 --- /dev/null +++ b/backend/libs/database/settings.entity.ts @@ -0,0 +1,20 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity() +export class BotSettings { + constructor(props?: Partial) { + Object.assign(this, props); + } + + @PrimaryGeneratedColumn('uuid') + public uuid!: string; + + @Column({ type: 'text', array: true }) + public messageTimes!: string[]; + + @Column() + public channel!: string; + + @Column({ default: false }) + public isActive!: boolean; +} diff --git a/backend/libs/libs.module.ts b/backend/libs/libs.module.ts index f369309..93885a3 100644 --- a/backend/libs/libs.module.ts +++ b/backend/libs/libs.module.ts @@ -1,13 +1,14 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { User } from './database/user.entity'; import { Admin } from './database/admin.entity'; -import { Post } from './database/post.entity'; import { Image } from './database/image.entity'; import { Payment } from './database/payment.entity'; +import { Post } from './database/post.entity'; import { ProxyUser } from './database/proxy_user.entity'; +import { BotSettings } from './database/settings.entity'; +import { User } from './database/user.entity'; @Module({ - imports: [TypeOrmModule.forFeature([User, Admin, Post, Image, Payment, ProxyUser])], + imports: [TypeOrmModule.forFeature([User, Admin, Post, Image, Payment, ProxyUser, BotSettings])], exports: [TypeOrmModule], }) export class LibsModule {} diff --git a/backend/package.json b/backend/package.json index 7e661ba..e12fd76 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,10 +22,13 @@ "dependencies": { "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.1.16", "@nestjs/typeorm": "^10.0.1", "dotenv": "^16.3.1", + "passport": "^0.7.0", + "passport-http-bearer": "^1.0.1", "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", @@ -38,6 +41,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/node": "^20.3.1", + "@types/passport-http-bearer": "^1.0.41", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", diff --git a/backend/pnpm-lock.yaml b/backend/pnpm-lock.yaml index 1bfc8ed..15c3456 100644 --- a/backend/pnpm-lock.yaml +++ b/backend/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@nestjs/core': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/passport': + specifier: ^10.0.3 + version: 10.0.3(@nestjs/common@10.2.8)(passport@0.7.0) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8) @@ -23,6 +26,12 @@ dependencies: dotenv: specifier: ^16.3.1 version: 16.3.1 + passport: + specifier: ^0.7.0 + version: 0.7.0 + passport-http-bearer: + specifier: ^1.0.1 + version: 1.0.1 pg: specifier: ^8.11.3 version: 8.11.3 @@ -55,6 +64,9 @@ devDependencies: '@types/node': specifier: ^20.3.1 version: 20.9.0 + '@types/passport-http-bearer': + specifier: ^1.0.41 + version: 1.0.41 '@types/supertest': specifier: ^2.0.12 version: 2.0.16 @@ -964,6 +976,16 @@ packages: reflect-metadata: 0.1.13 dev: false + /@nestjs/passport@10.0.3(@nestjs/common@10.2.8)(passport@0.7.0): + resolution: {integrity: sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + passport: ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 + dependencies: + '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) + passport: 0.7.0 + dev: false + /@nestjs/platform-express@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8): resolution: {integrity: sha512-WoSSVtwIRc5AdGMHWVzWZK4JZLT0f4o2xW8P9gQvcX+omL8W1kXCfY8GQYXNBG84XmBNYH8r0FtC8oMe/lH5NQ==} peerDependencies: @@ -1141,6 +1163,12 @@ packages: /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + /@types/accepts@1.3.7: + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + dependencies: + '@types/node': 20.9.0 + dev: true + /@types/babel__core@7.20.4: resolution: {integrity: sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==} dependencies: @@ -1183,10 +1211,23 @@ packages: '@types/node': 20.9.0 dev: true + /@types/content-disposition@0.5.8: + resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} + dev: true + /@types/cookiejar@2.1.4: resolution: {integrity: sha512-b698BLJ6kPVd6uhHsY7wlebZdrWPXYied883PDSzpJZYOP97EOn/oGdLCH3jJf157srkFReIZY5v0H1s8Dozrg==} dev: true + /@types/cookies@0.7.10: + resolution: {integrity: sha512-hmUCjAk2fwZVPPkkPBcI7jGLIR5mg4OVoNMBwU6aVsMm/iNPY7z9/R+x2fSwLt/ZXoGua6C5Zy2k5xOo9jUyhQ==} + dependencies: + '@types/connect': 3.4.38 + '@types/express': 4.17.21 + '@types/keygrip': 1.0.6 + '@types/node': 20.9.0 + dev: true + /@types/eslint-scope@3.7.7: resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} dependencies: @@ -1229,6 +1270,10 @@ packages: '@types/node': 20.9.0 dev: true + /@types/http-assert@1.5.5: + resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} + dev: true + /@types/http-errors@2.0.4: resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} dev: true @@ -1260,6 +1305,29 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true + /@types/keygrip@1.0.6: + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + dev: true + + /@types/koa-compose@3.2.8: + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + dependencies: + '@types/koa': 2.13.12 + dev: true + + /@types/koa@2.13.12: + resolution: {integrity: sha512-vAo1KuDSYWFDB4Cs80CHvfmzSQWeUb909aQib0C0aFx4sw0K9UZFz2m5jaEP+b3X1+yr904iQiruS0hXi31jbw==} + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.8 + '@types/cookies': 0.7.10 + '@types/http-assert': 1.5.5 + '@types/http-errors': 2.0.4 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 20.9.0 + dev: true + /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true @@ -1273,6 +1341,20 @@ packages: dependencies: undici-types: 5.26.5 + /@types/passport-http-bearer@1.0.41: + resolution: {integrity: sha512-ecW+9e8C+0id5iz3YZ+uIarsk/vaRPkKSajt1i1Am66t0mC9gDfQDKXZz9fnPOW2xKUufbmCSou4005VM94Feg==} + dependencies: + '@types/express': 4.17.21 + '@types/koa': 2.13.12 + '@types/passport': 1.0.16 + dev: true + + /@types/passport@1.0.16: + resolution: {integrity: sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==} + dependencies: + '@types/express': 4.17.21 + dev: true + /@types/qs@6.9.10: resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} dev: true @@ -4354,6 +4436,27 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + /passport-http-bearer@1.0.1: + resolution: {integrity: sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-strategy: 1.0.0 + dev: false + + /passport-strategy@1.0.0: + resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} + engines: {node: '>= 0.4.0'} + dev: false + + /passport@0.7.0: + resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + dev: false + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4397,6 +4500,10 @@ packages: engines: {node: '>=8'} dev: true + /pause@0.0.1: + resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} + dev: false + /pg-cloudflare@1.1.1: resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} requiresBuild: true diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 09548a3..f3f4e2e 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -4,23 +4,30 @@ import { config } from 'config'; import { LibsModule } from 'libs/libs.module'; import { AppController } from './app.controller'; import { AdminModule } from './modules/admin/admin.module'; +import { AuthModule } from './modules/auth/auth.module'; import { ImageModule } from './modules/image/image.module'; import { AppInitService } from './modules/initialization/app.init.service'; import { PostModule } from './modules/post/post.module'; import { ProxyModule } from './modules/proxy/proxy.module'; +import { SettingsModule } from './modules/settings/settings.module'; import { UserModule } from './modules/user/user.module'; @Module({ imports: [ + AuthModule, LibsModule, PostModule, AdminModule, UserModule, ImageModule, ProxyModule, + SettingsModule, TypeOrmModule.forRoot(config.database), ], controllers: [AppController], - providers: [AppInitService], + providers: [ + AppInitService, + // { provide: APP_GUARD, useClass: AuthGuard }, // Если будет необходима авторизация + ], }) export class AppModule {} diff --git a/backend/src/modules/admin/admin.service.ts b/backend/src/modules/admin/admin.service.ts index cfd9557..702e540 100644 --- a/backend/src/modules/admin/admin.service.ts +++ b/backend/src/modules/admin/admin.service.ts @@ -11,8 +11,12 @@ export class AdminService { async getAdmins() { try { this.logger.debug(`[admin.getAdmins]`); - const admins = await this.adminRepository.find(); - return admins; + const admins = await this.adminRepository.find({ relations: { user: true }, select: { user_id: true, user: { user_name: true } } }); + const result: { user_id: string; user_name: string }[] = admins.map((admin) => ({ + user_id: admin.user_id, + user_name: admin.user.user_name, + })); + return result; } catch (error) { this.logger.log(`[getAdmin] ${JSON.stringify({ error })}`); return []; diff --git a/backend/src/modules/auth/auth.guard.ts b/backend/src/modules/auth/auth.guard.ts new file mode 100644 index 0000000..fdd646f --- /dev/null +++ b/backend/src/modules/auth/auth.guard.ts @@ -0,0 +1,36 @@ +import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthService } from './auth.service'; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor( + private reflector: Reflector, + private readonly authService: AuthService, + ) {} + + canActivate(context: ExecutionContext) { + const request = context.switchToHttp().getRequest(); + const allowUnauthorizedRequest = this.reflector.get('allowUnauthorizedRequest', context.getHandler()); + + let token = this.extractTokenFromHeader(request.headers); + + if (!token) { + token = request.query.access_token || request.body.access_token; + } + if (allowUnauthorizedRequest || this.authService.authUserByToken(token)) return true; + throw new UnauthorizedException('Unathorized!'); + } + + private extractTokenFromHeader(headers: any): string | null { + if (headers && headers.authorization) { + const authHeader = headers.authorization as string; + const headerParts = authHeader.split(' '); + + if (headerParts.length === 2 && headerParts[0].toLowerCase() === 'bearer') { + return headerParts[1]; + } + } + return null; + } +} diff --git a/backend/src/modules/auth/auth.module.ts b/backend/src/modules/auth/auth.module.ts new file mode 100644 index 0000000..7d044f2 --- /dev/null +++ b/backend/src/modules/auth/auth.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; +import { AuthService } from './auth.service'; +import { HttpBearerStrategy } from './http-bearer.strategy'; + +@Module({ + imports: [PassportModule.register({ defaultStrategy: 'bearer' })], + providers: [HttpBearerStrategy, AuthService], + exports: [HttpBearerStrategy, AuthService], +}) +export class AuthModule {} diff --git a/backend/src/modules/auth/auth.service.ts b/backend/src/modules/auth/auth.service.ts new file mode 100644 index 0000000..e8409f4 --- /dev/null +++ b/backend/src/modules/auth/auth.service.ts @@ -0,0 +1,10 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { config } from 'config'; + +@Injectable() +export class AuthService { + private readonly logger: Logger = new Logger(AuthService.name); + authUserByToken(token: string) { + return token === config.server.access_token; + } +} diff --git a/backend/src/modules/auth/http-bearer.strategy.ts b/backend/src/modules/auth/http-bearer.strategy.ts new file mode 100644 index 0000000..fd2aeb1 --- /dev/null +++ b/backend/src/modules/auth/http-bearer.strategy.ts @@ -0,0 +1,18 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy } from 'passport-http-bearer'; +import { AuthService } from './auth.service'; +@Injectable() +export class HttpBearerStrategy extends PassportStrategy(Strategy) { + constructor(private readonly authService: AuthService) { + super(); + } + + async validate(token: string): Promise { + const user = await this.authService.authUserByToken(token); + if (!user) { + throw new UnauthorizedException(); + } + return user; + } +} diff --git a/backend/src/modules/initialization/app.init.service.ts b/backend/src/modules/initialization/app.init.service.ts index 9361f3e..d03df83 100644 --- a/backend/src/modules/initialization/app.init.service.ts +++ b/backend/src/modules/initialization/app.init.service.ts @@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Admin } from 'libs/database/admin.entity'; import { Image } from 'libs/database/image.entity'; import { Post } from 'libs/database/post.entity'; +import { BotSettings } from 'libs/database/settings.entity'; import { User } from 'libs/database/user.entity'; import { Repository } from 'typeorm'; @@ -13,6 +14,7 @@ export class AppInitService implements OnModuleInit { @InjectRepository(Admin) private adminRepository: Repository, @InjectRepository(Post) private postRepository: Repository, @InjectRepository(Image) private ImageRepository: Repository, + @InjectRepository(BotSettings) private BotSettingsRepository: Repository, ) {} async onModuleInit() {} diff --git a/backend/src/modules/settings/settings.controller.ts b/backend/src/modules/settings/settings.controller.ts new file mode 100644 index 0000000..0905ba8 --- /dev/null +++ b/backend/src/modules/settings/settings.controller.ts @@ -0,0 +1,42 @@ +import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ICreateBotSettingsProfile, IEditBotSettingsProfile } from './settings.dto'; +import { SettingsService } from './settings.service'; + +// Если нужна будет авторизация, для выключения авторизации на конкретном +// const AllowUnathorizedRequest = () => SetMetadata('allowUnathorizedRequest', true); +@ApiTags('Settings') +@Controller('settings') +export class SettingsController { + constructor(private settingsService: SettingsService) {} + + @ApiOperation({ description: 'Get settings for bot' }) + @Get() + async getSettings() { + return await this.settingsService.getSettings(); + } + + @ApiOperation({ description: 'Get all bot settings profiles' }) + @Get('profile') + async getProfiles() { + return await this.settingsService.getProfiles(); + } + + @ApiOperation({ description: 'Create new settings profile' }) + @Post('profile/new') + async newProfile(@Body() data: ICreateBotSettingsProfile) { + return await this.settingsService.newProfile(data); + } + + @ApiOperation({ description: 'Edit settings profile' }) + @Post('profile/edit') + async editProfile(@Body() data: IEditBotSettingsProfile) { + return await this.settingsService.editProfile(data); + } + + @ApiOperation({ description: 'Delete settings profile' }) + @Delete('profile/delete/:uuid') + async deleteProfile(@Param('uuid') uuid: string) { + return await this.settingsService.deleteProfile(uuid); + } +} diff --git a/backend/src/modules/settings/settings.dto.ts b/backend/src/modules/settings/settings.dto.ts new file mode 100644 index 0000000..6bb6429 --- /dev/null +++ b/backend/src/modules/settings/settings.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ICreateBotSettingsProfile { + @ApiProperty({ description: 'Channel', example: '@neurowoman_test' }) readonly channel: string; + @ApiProperty({ description: 'Post times', example: ['12:00', '14:00'] }) readonly postTimes: string[]; +} + +export class IEditBotSettingsProfile { + @ApiProperty({ description: 'Channel', example: '@neurowoman_test' }) readonly channel: string | undefined; + @ApiProperty({ description: 'Profile uuid', example: '96e7d1b3-20ca-46e8-ac68-e406f79b7eb9' }) readonly uuid: string; + @ApiProperty({ description: 'Post times', example: ['12:00', '14:00'] }) readonly postTimes: string[] | undefined; + @ApiProperty({ description: 'Is active?', example: false }) readonly isActive: boolean | undefined; +} diff --git a/backend/src/modules/settings/settings.module.ts b/backend/src/modules/settings/settings.module.ts new file mode 100644 index 0000000..bfac8c5 --- /dev/null +++ b/backend/src/modules/settings/settings.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { LibsModule } from 'libs/libs.module'; +import { SettingsController } from './settings.controller'; +import { SettingsService } from './settings.service'; + +@Module({ + imports: [LibsModule], + controllers: [SettingsController], + providers: [SettingsService], +}) +export class SettingsModule {} diff --git a/backend/src/modules/settings/settings.service.ts b/backend/src/modules/settings/settings.service.ts new file mode 100644 index 0000000..4353356 --- /dev/null +++ b/backend/src/modules/settings/settings.service.ts @@ -0,0 +1,62 @@ +import { HttpException, Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { BotSettings } from 'libs/database/settings.entity'; +import { Repository } from 'typeorm'; +import { ICreateBotSettingsProfile, IEditBotSettingsProfile } from './settings.dto'; +@Injectable() +export class SettingsService { + constructor(@InjectRepository(BotSettings) private botSettingsRepository: Repository) {} + private readonly logger: Logger = new Logger(SettingsService.name); + + async getSettings() { + this.logger.log('[settings.getSettings]'); + const settings = await this.botSettingsRepository.findOneBy({ isActive: true }); + if (settings) return settings; + this.logger.debug(`[settings.getSettings] No active settings found`); + throw new HttpException('No settings found', 404); + } + + async newProfile(data: ICreateBotSettingsProfile) { + this.logger.log(`[settings.newProfile] data: ${JSON.stringify(data)}`); + return await this.botSettingsRepository.save({ channel: data.channel, messageTimes: data.postTimes }); + } + + async getProfiles() { + this.logger.log(`[settings.getProfiles]`); + return await this.botSettingsRepository.find(); + } + + async editProfile(data: IEditBotSettingsProfile) { + this.logger.log(`[settings.editProfile] data: ${JSON.stringify(data)}`); + const editProfile = await this.botSettingsRepository.findOneBy({ uuid: data.uuid }); + if (!editProfile) { + this.logger.debug(`[settings.editProfile] No profile found`); + throw new HttpException('No profile found', 404); + } + data.channel ? (editProfile.channel = data.channel) : ''; + data.postTimes ? (editProfile.messageTimes = data.postTimes) : ''; + if (data.isActive !== undefined) { + editProfile.isActive = data.isActive; + if (data.isActive) { + const nowActive = await this.botSettingsRepository.findOneBy({ isActive: true }); + nowActive.isActive = false; + await this.botSettingsRepository.save(nowActive); + } + } + return await this.botSettingsRepository.save(editProfile); + } + + async deleteProfile(profile_uuid: string) { + this.logger.log(`[settings.deleteProfile] uuid: ${profile_uuid}`); + const deleteProfile = await this.botSettingsRepository.findOneBy({ uuid: profile_uuid }); + if (!deleteProfile) { + this.logger.debug(`[settings.deleteProfile] No profile found`); + throw new HttpException('No profile found', 404); + } + if (deleteProfile.isActive) { + this.logger.debug(`[settings.deleteProfile] Can't delete active profile`); + throw new HttpException("Can't delete active profile", 400); + } + return await this.botSettingsRepository.delete(deleteProfile.uuid); + } +} diff --git a/neuro-reply-bot-reworked b/neuro-reply-bot-reworked index 142d18b..43b1c5e 160000 --- a/neuro-reply-bot-reworked +++ b/neuro-reply-bot-reworked @@ -1 +1 @@ -Subproject commit 142d18bac35f49e7e26047fdf696eb7cb1c7f7b1 +Subproject commit 43b1c5ecf4bc1def0af2bc3750db4644715e7031