mirror of
https://github.com/n08i40k/schedule-parser-next.git
synced 2025-12-06 09:47:46 +03:00
1.2.0
Добавлена возможность заменять файл с расписанием. Добалена возможность давать доступ к end-point'ам только определённым ролям. Чуть-чуть меньше спагетти в объявлениях модулей.
This commit is contained in:
22
package-lock.json
generated
22
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
4
src/auth-role/auth-role.decorator.ts
Normal file
4
src/auth-role/auth-role.decorator.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Reflector } from "@nestjs/core";
|
||||
import { UserRoleDto } from "../dto/user.dto";
|
||||
|
||||
export const AuthRoles = Reflector.createDecorator<UserRoleDto[]>();
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -55,15 +55,16 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async signUp(signUpDto: SignUpReqDto): Promise<SignUpResDto> {
|
||||
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<SignInResDto> {
|
||||
const user = await this.usersService.findUnique({
|
||||
username: signInDto.username,
|
||||
username: signInDto.username.replaceAll(" ", ""),
|
||||
});
|
||||
|
||||
if (
|
||||
|
||||
29
src/dto/schedule-replacer.dto.ts
Normal file
29
src/dto/schedule-replacer.dto.ts
Normal file
@@ -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;
|
||||
}
|
||||
20
src/schedule-replacer/schedule-replacer.controller.spec.ts
Normal file
20
src/schedule-replacer/schedule-replacer.controller.spec.ts
Normal file
@@ -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>(
|
||||
ScheduleReplacerController,
|
||||
);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
106
src/schedule-replacer/schedule-replacer.controller.ts
Normal file
106
src/schedule-replacer/schedule-replacer.controller.ts
Normal file
@@ -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<void> {
|
||||
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<ScheduleReplacerResDto[]> {
|
||||
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<ClearScheduleReplacerResDto> {
|
||||
const resDto = { count: await this.scheduleReplaceService.clear() };
|
||||
|
||||
await this.scheduleService.refreshCache();
|
||||
|
||||
return resDto;
|
||||
}
|
||||
}
|
||||
15
src/schedule-replacer/schedule-replacer.module.ts
Normal file
15
src/schedule-replacer/schedule-replacer.module.ts
Normal file
@@ -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 {}
|
||||
18
src/schedule-replacer/schedule-replacer.service.spec.ts
Normal file
18
src/schedule-replacer/schedule-replacer.service.spec.ts
Normal file
@@ -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>(ScheduleReplacerService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
48
src/schedule-replacer/schedule-replacer.service.ts
Normal file
48
src/schedule-replacer/schedule-replacer.service.ts
Normal file
@@ -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<ScheduleReplacerDto | null> {
|
||||
const response = await this.prismaService.scheduleReplace.findUnique({
|
||||
where: { etag: etag },
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
return plainToClass(ScheduleReplacerDto, response);
|
||||
}
|
||||
|
||||
async clear(): Promise<number> {
|
||||
const count = await this.prismaService.scheduleReplace.count();
|
||||
await this.prismaService.scheduleReplace.deleteMany({});
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
async setByEtag(etag: string, buffer: Buffer): Promise<void> {
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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<JSDOM | null> {
|
||||
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;
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -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<Array<number>> = [];
|
||||
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<ScheduleParseResult> {
|
||||
async getSourceSchedule(): Promise<ScheduleParseResult> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user