From 32e06350ad72b6accf3bd49eba1a193de8f7bcec Mon Sep 17 00:00:00 2001 From: n08i40k Date: Thu, 3 Oct 2024 01:49:23 +0400 Subject: [PATCH] 1.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлена возможность заменять файл с расписанием. Добалена возможность давать доступ к end-point'ам только определённым ролям. Чуть-чуть меньше спагетти в объявлениях модулей. --- package-lock.json | 22 ++-- package.json | 6 +- prisma/schema.prisma | 6 + src/app.module.ts | 2 + src/auth-role/auth-role.decorator.ts | 4 + src/auth/auth.controller.ts | 2 +- src/auth/auth.guard.ts | 38 +++++-- src/auth/auth.module.ts | 6 +- src/auth/auth.service.ts | 13 ++- src/dto/schedule-replacer.dto.ts | 29 +++++ .../schedule-replacer.controller.spec.ts | 20 ++++ .../schedule-replacer.controller.ts | 106 ++++++++++++++++++ .../schedule-replacer.module.ts | 15 +++ .../schedule-replacer.service.spec.ts | 18 +++ .../schedule-replacer.service.ts | 48 ++++++++ .../schedule-parser/schedule-parser.ts | 4 + .../xls-downloader/basic-xls-downloader.ts | 30 ++++- src/schedule/schedule.module.ts | 8 +- src/schedule/schedule.service.ts | 25 ++++- src/users/users.module.ts | 5 +- 20 files changed, 361 insertions(+), 46 deletions(-) create mode 100644 src/auth-role/auth-role.decorator.ts create mode 100644 src/dto/schedule-replacer.dto.ts create mode 100644 src/schedule-replacer/schedule-replacer.controller.spec.ts create mode 100644 src/schedule-replacer/schedule-replacer.controller.ts create mode 100644 src/schedule-replacer/schedule-replacer.module.ts create mode 100644 src/schedule-replacer/schedule-replacer.service.spec.ts create mode 100644 src/schedule-replacer/schedule-replacer.service.ts diff --git a/package-lock.json b/package-lock.json index 4981d1f..8bdb4bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,11 +34,11 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", - "@types/axios": "^0.14.0", "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsdom": "^21.1.7", + "@types/multer": "^1.4.12", "@types/node": "^20.16.5", "@types/supertest": "^6.0.0", "@types/uuid": "^10.0.0", @@ -2078,16 +2078,6 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "node_modules/@types/axios": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", - "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", - "deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!", - "dev": true, - "dependencies": { - "axios": "*" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2279,6 +2269,16 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "20.16.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", diff --git a/package.json b/package.json index ac57686..f38bb9e 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "schedule-parser-next", - "version": "1.1.1", + "version": "1.2.0", "description": "", - "author": "", + "author": "N08I40K", "private": true, "license": "UNLICENSED", "scripts": { @@ -45,11 +45,11 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", - "@types/axios": "^0.14.0", "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsdom": "^21.1.7", + "@types/multer": "^1.4.12", "@types/node": "^20.16.5", "@types/supertest": "^6.0.0", "@types/uuid": "^10.0.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index dbe2402..4316f59 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,6 +13,12 @@ datasource db { url = env("DATABASE_URL") } +model ScheduleReplace { + id String @id @default(auto()) @map("_id") @db.ObjectId + etag String @unique + data Bytes +} + enum UserRole { STUDENT TEACHER diff --git a/src/app.module.ts b/src/app.module.ts index b418f3e..e63b128 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,6 +3,7 @@ import { AuthModule } from "./auth/auth.module"; import { UsersModule } from "./users/users.module"; import { ScheduleModule } from "./schedule/schedule.module"; import { CacheModule } from "@nestjs/cache-manager"; +import { ScheduleReplacerModule } from "./schedule-replacer/schedule-replacer.module"; @Module({ imports: [ @@ -10,6 +11,7 @@ import { CacheModule } from "@nestjs/cache-manager"; UsersModule, ScheduleModule, CacheModule.register({ ttl: 5 * 60 * 1000, isGlobal: true }), + ScheduleReplacerModule, ], controllers: [], providers: [], diff --git a/src/auth-role/auth-role.decorator.ts b/src/auth-role/auth-role.decorator.ts new file mode 100644 index 0000000..d97157f --- /dev/null +++ b/src/auth-role/auth-role.decorator.ts @@ -0,0 +1,4 @@ +import { Reflector } from "@nestjs/core"; +import { UserRoleDto } from "../dto/user.dto"; + +export const AuthRoles = Reflector.createDecorator(); diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index e14ea0c..a77ad31 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -73,7 +73,7 @@ export class AuthController { async signUp(@Body() signUpDto: SignUpReqDto) { if ( !(await this.scheduleService.getGroupNames()).names.includes( - signUpDto.group, + signUpDto.group.replaceAll(" ", ""), ) ) { throw new NotFoundException( diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index ccd9e29..f5a249b 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -1,18 +1,23 @@ import { CanActivate, ExecutionContext, + ForbiddenException, Injectable, UnauthorizedException, } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; import { Request } from "express"; import { UsersService } from "../users/users.service"; +import { Reflector } from "@nestjs/core"; +import { AuthRoles } from "../auth-role/auth-role.decorator"; +import { isJWT } from "class-validator"; @Injectable() export class AuthGuard implements CanActivate { constructor( private readonly usersService: UsersService, private readonly jwtService: JwtService, + private readonly reflector: Reflector, ) {} public static extractTokenFromRequest(req: Request): string { @@ -28,18 +33,27 @@ export class AuthGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const token = AuthGuard.extractTokenFromRequest(request); - if (!token) - try { - if ( - !(await this.jwtService.verifyAsync(token)) || - !(await this.usersService.contains({ accessToken: token })) - ) { - // noinspection ExceptionCaughtLocallyJS - throw new Error(); - } - } catch { - throw new UnauthorizedException("Указан неверный токен!"); - } + let jwtUser: { id: string } | null = null; + + if ( + !isJWT(token) || + !(jwtUser = await this.jwtService + .verifyAsync(token) + .catch(() => null)) + ) + throw new UnauthorizedException(); + + const user = await this.usersService.findUnique({ id: jwtUser.id }); + if (!user || user.accessToken !== token) + throw new UnauthorizedException(); + + const acceptableRoles = this.reflector.get( + AuthRoles, + context.getHandler(), + ); + + if (acceptableRoles != null && !acceptableRoles.includes(user.role)) + throw new ForbiddenException(); return true; } diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 16ccf74..355c8a0 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -4,20 +4,20 @@ import { jwtConstants } from "../contants"; import { AuthService } from "./auth.service"; import { AuthController } from "./auth.controller"; import { UsersModule } from "../users/users.module"; -import { UsersService } from "../users/users.service"; import { PrismaService } from "../prisma/prisma.service"; -import { ScheduleService } from "../schedule/schedule.service"; +import { ScheduleModule } from "../schedule/schedule.module"; @Module({ imports: [ UsersModule, + ScheduleModule, JwtModule.register({ global: true, secret: jwtConstants.secret, signOptions: { expiresIn: "720h" }, }), ], - providers: [AuthService, UsersService, PrismaService, ScheduleService], + providers: [AuthService, PrismaService], controllers: [AuthController], exports: [AuthService], }) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index a9e26e7..dae56c9 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -55,15 +55,16 @@ export class AuthService { } async signUp(signUpDto: SignUpReqDto): Promise { + const group = signUpDto.group.replaceAll(" ", ""); + const username = signUpDto.username.replaceAll(" ", ""); + if ( ![UserRoleDto.STUDENT, UserRoleDto.TEACHER].includes(signUpDto.role) ) { throw new NotAcceptableException("Передана неизвестная роль"); } - if ( - await this.usersService.contains({ username: signUpDto.username }) - ) { + if (await this.usersService.contains({ username: username })) { throw new ConflictException( "Пользователь с таким именем уже существует!", ); @@ -74,14 +75,14 @@ export class AuthService { const input: Prisma.UserCreateInput = { id: id, - username: signUpDto.username, + username: username, salt: salt, password: await hash(signUpDto.password, salt), accessToken: await this.jwtService.signAsync({ id: id, }), role: signUpDto.role as UserRole, - group: signUpDto.group, + group: group, }; return this.usersService.create(input).then((user) => { @@ -94,7 +95,7 @@ export class AuthService { async signIn(signInDto: SignInReqDto): Promise { const user = await this.usersService.findUnique({ - username: signInDto.username, + username: signInDto.username.replaceAll(" ", ""), }); if ( diff --git a/src/dto/schedule-replacer.dto.ts b/src/dto/schedule-replacer.dto.ts new file mode 100644 index 0000000..8b99426 --- /dev/null +++ b/src/dto/schedule-replacer.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty, PickType } from "@nestjs/swagger"; +import { IsNumber, IsObject, IsString } from "class-validator"; + +export class ScheduleReplacerDto { + @ApiProperty({ description: "Etag заменяемого расписания" }) + @IsString() + etag: string; + + @ApiProperty({ description: "Данные файла расписания" }) + @IsObject() + data: ArrayBuffer; +} + +export class ScheduleReplacerResDto extends PickType(ScheduleReplacerDto, [ + "etag", +]) { + @ApiProperty({ example: 1405, description: "Размер файла в байтах" }) + @IsNumber() + size: number; +} + +export class ClearScheduleReplacerResDto { + @ApiProperty({ + example: 1, + description: "Количество удалённых заменителей расписания", + }) + @IsNumber() + count: number; +} diff --git a/src/schedule-replacer/schedule-replacer.controller.spec.ts b/src/schedule-replacer/schedule-replacer.controller.spec.ts new file mode 100644 index 0000000..7c592f1 --- /dev/null +++ b/src/schedule-replacer/schedule-replacer.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { ScheduleReplacerController } from "./schedule-replacer.controller"; + +describe("ScheduleReplacerController", () => { + let controller: ScheduleReplacerController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ScheduleReplacerController], + }).compile(); + + controller = module.get( + ScheduleReplacerController, + ); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/schedule-replacer/schedule-replacer.controller.ts b/src/schedule-replacer/schedule-replacer.controller.ts new file mode 100644 index 0000000..834f30d --- /dev/null +++ b/src/schedule-replacer/schedule-replacer.controller.ts @@ -0,0 +1,106 @@ +import { + BadRequestException, + Controller, + Get, + HttpCode, + HttpStatus, + Post, + UploadedFile, + UseGuards, + UseInterceptors, +} from "@nestjs/common"; +import { AuthGuard } from "src/auth/auth.guard"; +import { + ClearScheduleReplacerResDto, + ScheduleReplacerResDto, +} from "../dto/schedule-replacer.dto"; +import { AuthRoles } from "../auth-role/auth-role.decorator"; +import { UserRoleDto } from "../dto/user.dto"; +import { ScheduleReplacerService } from "./schedule-replacer.service"; +import { ScheduleService } from "../schedule/schedule.service"; +import { FileInterceptor } from "@nestjs/platform-express"; +import { + ApiExtraModels, + ApiOkResponse, + ApiOperation, + refs, +} from "@nestjs/swagger"; +import { ResultDto } from "src/utility/validation/class-validator.interceptor"; + +@Controller("/api/v1/schedule-replacer") +@UseGuards(AuthGuard) +export class ScheduleReplacerController { + constructor( + private readonly scheduleService: ScheduleService, + private readonly scheduleReplaceService: ScheduleReplacerService, + ) {} + + @ApiOperation({ + description: "Замена текущего расписание на новое", + tags: ["schedule", "replacer"], + }) + @ApiOkResponse({ description: "Замена прошла успешно" }) + @Post("set") + @HttpCode(HttpStatus.OK) + @AuthRoles([UserRoleDto.ADMIN]) + @ResultDto(null) + @UseInterceptors( + FileInterceptor("file", { limits: { fileSize: 1024 * 1024 } }), + ) + async setSchedule( + @UploadedFile() file: Express.Multer.File, + ): Promise { + if (!file) throw new BadRequestException("Файл отсутствует"); + if (file.mimetype !== "application/vnd.ms-excel") + throw new BadRequestException("Некорректный тип файла"); + + const etag = (await this.scheduleService.getSourceSchedule()).etag; + await this.scheduleReplaceService.setByEtag(etag, file.buffer); + await this.scheduleService.refreshCache(); + } + + @ApiExtraModels(ScheduleReplacerResDto) + @ApiOperation({ + description: "Получение списка заменителей расписания", + tags: ["schedule", "replacer"], + }) + @ApiOkResponse({ description: "Список получен успешно" }) // TODO: ааа(((( + @Get("get") + @HttpCode(HttpStatus.OK) + @AuthRoles([UserRoleDto.ADMIN]) + @ResultDto(null) // TODO: Как нибудь сделать проверку в таких случаях + async getReplacers(): Promise { + const etag = (await this.scheduleService.getSourceSchedule()).etag; + + const replacer = await this.scheduleReplaceService.getByEtag(etag); + if (!replacer) return []; + + return [ + { + etag: replacer.etag, + size: replacer.data.byteLength, + }, + ]; + } + + @ApiExtraModels(ClearScheduleReplacerResDto) + @ApiOperation({ + description: "Удаление всех замен расписаний", + tags: ["schedule", "replacer"], + }) + @ApiOkResponse({ + description: "Отчистка прошла успешно", + schema: refs(ClearScheduleReplacerResDto)[0], + }) + @Post("clear") + @HttpCode(HttpStatus.OK) + @AuthRoles([UserRoleDto.ADMIN]) + @ResultDto(ClearScheduleReplacerResDto) + async clear(): Promise { + const resDto = { count: await this.scheduleReplaceService.clear() }; + + await this.scheduleService.refreshCache(); + + return resDto; + } +} diff --git a/src/schedule-replacer/schedule-replacer.module.ts b/src/schedule-replacer/schedule-replacer.module.ts new file mode 100644 index 0000000..9ef2358 --- /dev/null +++ b/src/schedule-replacer/schedule-replacer.module.ts @@ -0,0 +1,15 @@ +import { Module } from "@nestjs/common"; +import { ScheduleReplacerService } from "./schedule-replacer.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { ScheduleReplacerController } from "./schedule-replacer.controller"; +import { ScheduleModule } from "../schedule/schedule.module"; +import { AuthService } from "../auth/auth.service"; +import { UsersModule } from "../users/users.module"; + +@Module({ + imports: [ScheduleModule, UsersModule], + providers: [AuthService, PrismaService, ScheduleReplacerService], + exports: [ScheduleReplacerService], + controllers: [ScheduleReplacerController], +}) +export class ScheduleReplacerModule {} diff --git a/src/schedule-replacer/schedule-replacer.service.spec.ts b/src/schedule-replacer/schedule-replacer.service.spec.ts new file mode 100644 index 0000000..c4305cd --- /dev/null +++ b/src/schedule-replacer/schedule-replacer.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { ScheduleReplacerService } from "./schedule-replacer.service"; + +describe("ScheduleReplacerService", () => { + let service: ScheduleReplacerService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ScheduleReplacerService], + }).compile(); + + service = module.get(ScheduleReplacerService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/schedule-replacer/schedule-replacer.service.ts b/src/schedule-replacer/schedule-replacer.service.ts new file mode 100644 index 0000000..3df4a09 --- /dev/null +++ b/src/schedule-replacer/schedule-replacer.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { ScheduleReplacerDto } from "../dto/schedule-replacer.dto"; +import { plainToClass } from "class-transformer"; + +@Injectable() +export class ScheduleReplacerService { + constructor(private readonly prismaService: PrismaService) {} + + async getByEtag(etag: string): Promise { + const response = await this.prismaService.scheduleReplace.findUnique({ + where: { etag: etag }, + }); + if (response == null) return null; + + return plainToClass(ScheduleReplacerDto, response); + } + + async clear(): Promise { + const count = await this.prismaService.scheduleReplace.count(); + await this.prismaService.scheduleReplace.deleteMany({}); + + return count; + } + + async setByEtag(etag: string, buffer: Buffer): Promise { + if ( + (await this.prismaService.scheduleReplace.count({ + where: { etag: etag }, + })) > 0 + ) { + await this.prismaService.scheduleReplace.update({ + where: { etag: etag }, + data: { + data: buffer, + }, + }); + return; + } + + await this.prismaService.scheduleReplace.create({ + data: { + etag: etag, + data: buffer, + }, + }); + } +} diff --git a/src/schedule/internal/schedule-parser/schedule-parser.ts b/src/schedule/internal/schedule-parser/schedule-parser.ts index 167c031..051ba3e 100644 --- a/src/schedule/internal/schedule-parser/schedule-parser.ts +++ b/src/schedule/internal/schedule-parser/schedule-parser.ts @@ -290,6 +290,10 @@ export class ScheduleParser { const llesson = lday.lessons[lessonIdx]; // noinspection SpellCheckingInspection const rlesson = rday.lessons[lessonIdx]; + + if (llesson === null && rlesson === null) continue; + if (!llesson || !rlesson) return false; + if ( llesson.name.length > 0 && (llesson.name !== rlesson.name || diff --git a/src/schedule/internal/xls-downloader/basic-xls-downloader.ts b/src/schedule/internal/xls-downloader/basic-xls-downloader.ts index b0034e0..eaa98ac 100644 --- a/src/schedule/internal/xls-downloader/basic-xls-downloader.ts +++ b/src/schedule/internal/xls-downloader/basic-xls-downloader.ts @@ -9,11 +9,22 @@ import { NotAcceptableException, ServiceUnavailableException, } from "@nestjs/common"; +import { ScheduleReplacerService } from "../../../schedule-replacer/schedule-replacer.service"; +import { Error } from "mongoose"; +import * as crypto from "crypto"; export class BasicXlsDownloader extends XlsDownloaderBase { cache: XlsDownloaderResult | null = null; preparedData: { downloadLink: string; updateDate: string } | null = null; + + private cacheHash: string = "0000000000000000000000000000000000000000"; + private lastUpdate: number = 0; + private scheduleReplacerService: ScheduleReplacerService | null = null; + + setScheduleReplacerService(service: ScheduleReplacerService) { + this.scheduleReplacerService = service; + } private async getDOM(preparedData: any): Promise { try { @@ -103,17 +114,32 @@ export class BasicXlsDownloader extends XlsDownloaderBase { ${response.statusText}`); } + const replacer = await this.scheduleReplacerService.getByEtag( + response.headers["etag"]!, + ); + + const fileData: ArrayBuffer = replacer + ? replacer.data + : response.data.buffer; + + const fileDataHash = crypto + .createHash("sha1") + .update(Buffer.from(fileData).toString("base64")) + .digest("hex"); + const result: XlsDownloaderResult = { - fileData: response.data.buffer, + fileData: fileData, updateDate: this.preparedData.updateDate, etag: response.headers["etag"], new: this.cacheMode === XlsDownloaderCacheMode.NONE ? true - : this.cache?.etag !== response.headers["etag"], + : this.cacheHash !== fileDataHash, updateRequired: this.isUpdateRequired(), }; + this.cacheHash = fileDataHash; + if (this.cacheMode !== XlsDownloaderCacheMode.NONE) this.cache = result; return result; diff --git a/src/schedule/schedule.module.ts b/src/schedule/schedule.module.ts index a7c62a8..cdbd6b6 100644 --- a/src/schedule/schedule.module.ts +++ b/src/schedule/schedule.module.ts @@ -3,9 +3,15 @@ import { ScheduleService } from "./schedule.service"; import { ScheduleController } from "./schedule.controller"; import { UsersService } from "../users/users.service"; import { PrismaService } from "../prisma/prisma.service"; +import { ScheduleReplacerService } from "../schedule-replacer/schedule-replacer.service"; @Module({ - providers: [ScheduleService, UsersService, PrismaService], + providers: [ + ScheduleService, + ScheduleReplacerService, + UsersService, + PrismaService, + ], controllers: [ScheduleController], exports: [ScheduleService], }) diff --git a/src/schedule/schedule.service.ts b/src/schedule/schedule.service.ts index af28120..5dab6b8 100644 --- a/src/schedule/schedule.service.ts +++ b/src/schedule/schedule.service.ts @@ -17,6 +17,7 @@ import { Cache, CACHE_MANAGER } from "@nestjs/cache-manager"; import { instanceToPlain } from "class-transformer"; import { cacheGetOrFill } from "../utility/cache.util"; import * as crypto from "crypto"; +import { ScheduleReplacerService } from "../schedule-replacer/schedule-replacer.service"; @Injectable() export class ScheduleService { @@ -33,7 +34,18 @@ export class ScheduleService { private lastChangedDays: Array> = []; private scheduleUpdatedAt: Date = new Date(0); - constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {} + constructor( + @Inject(CACHE_MANAGER) private readonly cacheManager: Cache, + private readonly scheduleReplacerService: ScheduleReplacerService, + ) { + const xlsDownloader = this.scheduleParser.getXlsDownloader(); + + if (xlsDownloader instanceof BasicXlsDownloader) { + xlsDownloader.setScheduleReplacerService( + this.scheduleReplacerService, + ); + } + } getCacheStatus(): CacheStatusDto { return { @@ -45,7 +57,7 @@ export class ScheduleService { }; } - private async getSourceSchedule(): Promise { + async getSourceSchedule(): Promise { return cacheGetOrFill(this.cacheManager, "sourceSchedule", async () => { const schedule = await this.scheduleParser.getSchedule(); schedule.groups = ScheduleService.toObject( @@ -146,10 +158,13 @@ export class ScheduleService { await this.scheduleParser .getXlsDownloader() .setPreparedData(siteMainPageDto.mainPage); - - await this.cacheManager.reset(); - await this.getSourceSchedule(); + await this.refreshCache(); return this.getCacheStatus(); } + + async refreshCache() { + await this.cacheManager.reset(); + await this.getSourceSchedule(); + } } diff --git a/src/users/users.module.ts b/src/users/users.module.ts index 6f06a92..40a45a7 100644 --- a/src/users/users.module.ts +++ b/src/users/users.module.ts @@ -3,10 +3,11 @@ import { UsersService } from "./users.service"; import { PrismaService } from "../prisma/prisma.service"; import { UsersController } from "./users.controller"; import { AuthService } from "../auth/auth.service"; -import { ScheduleService } from "../schedule/schedule.service"; +import { ScheduleModule } from "../schedule/schedule.module"; @Module({ - providers: [PrismaService, UsersService, AuthService, ScheduleService], + imports: [ScheduleModule], + providers: [PrismaService, UsersService, AuthService], exports: [UsersService], controllers: [UsersController], })