mirror of
https://github.com/n08i40k/schedule-parser-next.git
synced 2025-12-06 09:47:46 +03:00
Переход на postgres
This commit is contained in:
@@ -9,29 +9,26 @@ generator client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "mongodb"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
model ScheduleReplace {
|
|
||||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
||||||
etag String @unique
|
|
||||||
data Bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
enum UserRole {
|
enum UserRole {
|
||||||
STUDENT
|
STUDENT
|
||||||
TEACHER
|
TEACHER
|
||||||
ADMIN
|
ADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
type FCM {
|
model FCM {
|
||||||
token String
|
token String
|
||||||
topics String[]
|
topics String[]
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @map("_id") @db.ObjectId
|
id String @id
|
||||||
//
|
//
|
||||||
username String @unique
|
username String @unique
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -65,9 +65,10 @@ export class FirebaseAdminController {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (user.fcm?.token === token) return;
|
if (user.fcm?.token === token) return;
|
||||||
|
|
||||||
const updatedUser = (
|
const updatedUser = await this.firebaseAdminService.updateToken(
|
||||||
await this.firebaseAdminService.updateToken(user, token)
|
user,
|
||||||
).userDto;
|
token,
|
||||||
|
);
|
||||||
|
|
||||||
await this.firebaseAdminService
|
await this.firebaseAdminService
|
||||||
.subscribe(updatedUser, new Set(), true)
|
.subscribe(updatedUser, new Set(), true)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { UsersService } from "../users/users.service";
|
|||||||
import User from "../users/entity/user.entity";
|
import User from "../users/entity/user.entity";
|
||||||
import { TokenMessage } from "firebase-admin/lib/messaging/messaging-api";
|
import { TokenMessage } from "firebase-admin/lib/messaging/messaging-api";
|
||||||
import FCM from "../users/entity/fcm-user.entity";
|
import FCM from "../users/entity/fcm-user.entity";
|
||||||
import { plainToInstance } from "class-transformer";
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FirebaseAdminService implements OnModuleInit {
|
export class FirebaseAdminService implements OnModuleInit {
|
||||||
@@ -47,39 +46,47 @@ export class FirebaseAdminService implements OnModuleInit {
|
|||||||
await this.messaging.send(message);
|
await this.messaging.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFcmOrDefault(user: User, token: string): FCM {
|
private async getFcm(user: User, token: string): Promise<FCM> {
|
||||||
if (!user.fcm) {
|
const userToFCM = (user: User) =>
|
||||||
return plainToInstance(FCM, {
|
user.fcm ? FCM.fromObject(user.fcm) : null;
|
||||||
token: token,
|
|
||||||
topics: [],
|
const fcm = await this.usersService
|
||||||
} as FCM);
|
.findUnique({
|
||||||
|
where: { id: user.id },
|
||||||
|
include: { fcm: true },
|
||||||
|
})
|
||||||
|
.then(userToFCM);
|
||||||
|
|
||||||
|
if (fcm) return FCM.fromObject(fcm);
|
||||||
|
|
||||||
|
return await this.usersService
|
||||||
|
.update({
|
||||||
|
where: { id: user.id },
|
||||||
|
data: { fcm: { create: { token: token, topics: [] } } },
|
||||||
|
include: { fcm: true },
|
||||||
|
})
|
||||||
|
.then(userToFCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user.fcm;
|
async updateToken(user: User, token: string): Promise<User> {
|
||||||
|
const fcm = await this.getFcm(user, token);
|
||||||
|
|
||||||
|
if (user.fcm !== null) {
|
||||||
|
if (fcm.token === token) {
|
||||||
|
user.fcm = fcm;
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateToken(
|
|
||||||
user: User,
|
|
||||||
token: string,
|
|
||||||
): Promise<{ userDto: User; isNew: boolean }> {
|
|
||||||
const isNew = user.fcm === null;
|
|
||||||
const fcm = this.getFcmOrDefault(user, token);
|
|
||||||
|
|
||||||
if (!isNew) {
|
|
||||||
if (fcm.token === token) return { userDto: user, isNew: false };
|
|
||||||
|
|
||||||
for (const topic of fcm.topics)
|
for (const topic of fcm.topics)
|
||||||
await this.messaging.subscribeToTopic(token, topic);
|
await this.messaging.subscribeToTopic(token, topic);
|
||||||
fcm.token = token;
|
fcm.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return await this.usersService.update({
|
||||||
userDto: await this.usersService.update({
|
|
||||||
where: { id: user.id },
|
where: { id: user.id },
|
||||||
data: { fcm: fcm },
|
data: { fcm: { update: { token: fcm.token } } },
|
||||||
}),
|
include: { fcm: true },
|
||||||
isNew: isNew,
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async unsubscribe(user: User, topics: Set<string>): Promise<User> {
|
async unsubscribe(user: User, topics: Set<string>): Promise<User> {
|
||||||
@@ -100,7 +107,8 @@ export class FirebaseAdminService implements OnModuleInit {
|
|||||||
|
|
||||||
return await this.usersService.update({
|
return await this.usersService.update({
|
||||||
where: { id: user.id },
|
where: { id: user.id },
|
||||||
data: { fcm: fcm },
|
data: { fcm: { update: { topics: fcm.topics } } },
|
||||||
|
include: { fcm: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,11 +134,10 @@ export class FirebaseAdminService implements OnModuleInit {
|
|||||||
|
|
||||||
if (newTopics.size === fcm.topics.length) return user;
|
if (newTopics.size === fcm.topics.length) return user;
|
||||||
|
|
||||||
fcm.topics = Array.from(newTopics);
|
|
||||||
|
|
||||||
return await this.usersService.update({
|
return await this.usersService.update({
|
||||||
where: { id: user.id },
|
where: { id: user.id },
|
||||||
data: { fcm: fcm },
|
data: { fcm: { update: { topics: Array.from(newTopics) } } },
|
||||||
|
include: { fcm: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import Lesson from "../../entities/lesson.entity";
|
|||||||
import Day from "../../entities/day.entity";
|
import Day from "../../entities/day.entity";
|
||||||
import Group from "../../entities/group.entity";
|
import Group from "../../entities/group.entity";
|
||||||
import * as assert from "node:assert";
|
import * as assert from "node:assert";
|
||||||
import { ScheduleReplacerService } from "../../schedule-replacer.service";
|
|
||||||
import Teacher from "../../entities/teacher.entity";
|
import Teacher from "../../entities/teacher.entity";
|
||||||
import TeacherDay from "../../entities/teacher-day.entity";
|
import TeacherDay from "../../entities/teacher-day.entity";
|
||||||
import TeacherLesson from "../../entities/teacher-lesson.entity";
|
import TeacherLesson from "../../entities/teacher-lesson.entity";
|
||||||
@@ -23,8 +22,8 @@ import {
|
|||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { ToMap } from "create-map-transform-fn";
|
|
||||||
import { ClassProperties } from "../../../utility/class-trasformer/class-transformer-ctor";
|
import { ClassProperties } from "../../../utility/class-trasformer/class-transformer-ctor";
|
||||||
|
import { ToMap } from "create-map-transform-fn";
|
||||||
|
|
||||||
type InternalId = {
|
type InternalId = {
|
||||||
/**
|
/**
|
||||||
@@ -132,11 +131,9 @@ export class ScheduleParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param xlsDownloader - класс для загрузки расписания с сайта политехникума
|
* @param xlsDownloader - класс для загрузки расписания с сайта политехникума
|
||||||
* @param scheduleReplacerService - сервис для подмены расписания
|
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly xlsDownloader: XlsDownloaderInterface,
|
private readonly xlsDownloader: XlsDownloaderInterface,
|
||||||
private readonly scheduleReplacerService?: ScheduleReplacerService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,20 +434,10 @@ export class ScheduleParser {
|
|||||||
|
|
||||||
assert(headData.type === "success");
|
assert(headData.type === "success");
|
||||||
|
|
||||||
const replacer = this.scheduleReplacerService
|
if (this.lastResult && this.lastResult.etag === headData.etag)
|
||||||
? await this.scheduleReplacerService.getByEtag(headData.etag)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (this.lastResult && this.lastResult.etag === headData.etag) {
|
|
||||||
if (!replacer) return this.lastResult;
|
|
||||||
|
|
||||||
if (this.lastResult.replacerId === replacer.id)
|
|
||||||
return this.lastResult;
|
return this.lastResult;
|
||||||
}
|
|
||||||
|
|
||||||
const buffer = async () => {
|
const buffer = async () => {
|
||||||
if (replacer) return replacer.data;
|
|
||||||
|
|
||||||
const downloadData = await this.xlsDownloader.fetch(false);
|
const downloadData = await this.xlsDownloader.fetch(false);
|
||||||
this.xlsDownloader.verifyFetchResult(downloadData);
|
this.xlsDownloader.verifyFetchResult(downloadData);
|
||||||
|
|
||||||
@@ -605,7 +592,7 @@ export class ScheduleParser {
|
|||||||
uploadedAt: headData.uploadedAt,
|
uploadedAt: headData.uploadedAt,
|
||||||
|
|
||||||
etag: headData.etag,
|
etag: headData.etag,
|
||||||
replacerId: replacer?.id,
|
replacerId: null,
|
||||||
|
|
||||||
groups: groups,
|
groups: groups,
|
||||||
teachers: teachers,
|
teachers: teachers,
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
import {
|
|
||||||
BadRequestException,
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
HttpCode,
|
|
||||||
HttpStatus,
|
|
||||||
Post,
|
|
||||||
UploadedFile,
|
|
||||||
UseGuards,
|
|
||||||
UseInterceptors,
|
|
||||||
} from "@nestjs/common";
|
|
||||||
import { AuthGuard } from "src/auth/auth.guard";
|
|
||||||
import { AuthRoles } from "../auth/auth-role.decorator";
|
|
||||||
import { ScheduleReplacerService } from "./schedule-replacer.service";
|
|
||||||
import { FileInterceptor } from "@nestjs/platform-express";
|
|
||||||
import {
|
|
||||||
ApiBearerAuth,
|
|
||||||
ApiOperation,
|
|
||||||
ApiResponse,
|
|
||||||
ApiTags,
|
|
||||||
} from "@nestjs/swagger";
|
|
||||||
import { ResultDto } from "src/utility/validation/class-validator.interceptor";
|
|
||||||
import { plainToInstance } from "class-transformer";
|
|
||||||
import { ScheduleService } from "./schedule.service";
|
|
||||||
import UserRole from "../users/user-role.enum";
|
|
||||||
import ReplacerDto from "./dto/replacer.dto";
|
|
||||||
import ClearReplacerDto from "./dto/clear-replacer.dto";
|
|
||||||
|
|
||||||
@ApiTags("v1/schedule-replacer")
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@Controller({ path: "schedule-replacer", version: "1" })
|
|
||||||
@UseGuards(AuthGuard)
|
|
||||||
export class ScheduleReplacerController {
|
|
||||||
constructor(
|
|
||||||
private readonly scheduleService: ScheduleService,
|
|
||||||
private readonly scheduleReplaceService: ScheduleReplacerService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@ApiOperation({ description: "Замена текущего расписание на новое" })
|
|
||||||
@ApiResponse({
|
|
||||||
status: HttpStatus.OK,
|
|
||||||
description: "Замена прошла успешно",
|
|
||||||
})
|
|
||||||
@Post("set")
|
|
||||||
@HttpCode(HttpStatus.OK)
|
|
||||||
@AuthRoles([UserRole.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ description: "Получение списка заменителей расписания" })
|
|
||||||
@ApiResponse({
|
|
||||||
status: HttpStatus.OK,
|
|
||||||
description: "Список получен успешно",
|
|
||||||
})
|
|
||||||
@Get("get")
|
|
||||||
@HttpCode(HttpStatus.OK)
|
|
||||||
@AuthRoles([UserRole.ADMIN])
|
|
||||||
@ResultDto(null) // TODO: Как нибудь сделать проверку в таких случаях
|
|
||||||
async getReplacers(): Promise<ReplacerDto[]> {
|
|
||||||
return await this.scheduleReplaceService.getAll().then((result) => {
|
|
||||||
return result.map((replacer) => {
|
|
||||||
return plainToInstance(ReplacerDto, {
|
|
||||||
etag: replacer.etag,
|
|
||||||
size: replacer.data.byteLength,
|
|
||||||
} as ReplacerDto);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ description: "Удаление всех замен расписаний" })
|
|
||||||
@ApiResponse({
|
|
||||||
status: HttpStatus.OK,
|
|
||||||
description: "Отчистка прошла успешно",
|
|
||||||
type: ClearReplacerDto,
|
|
||||||
})
|
|
||||||
@Post("clear")
|
|
||||||
@HttpCode(HttpStatus.OK)
|
|
||||||
@AuthRoles([UserRole.ADMIN])
|
|
||||||
@ResultDto(ClearReplacerDto)
|
|
||||||
async clear(): Promise<ClearReplacerDto> {
|
|
||||||
const response = { count: await this.scheduleReplaceService.clear() };
|
|
||||||
|
|
||||||
await this.scheduleService.refreshCache();
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
|
||||||
import { PrismaService } from "../prisma/prisma.service";
|
|
||||||
import SetScheduleReplacerDto from "./dto/set-schedule-replacer.dto";
|
|
||||||
import { plainToInstance } from "class-transformer";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ScheduleReplacerService {
|
|
||||||
constructor(private readonly prismaService: PrismaService) {}
|
|
||||||
|
|
||||||
async hasByEtag(etag: string): Promise<boolean> {
|
|
||||||
return (
|
|
||||||
(await this.prismaService.scheduleReplace.count({
|
|
||||||
where: { etag: etag },
|
|
||||||
})) > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getByEtag(etag: string): Promise<SetScheduleReplacerDto | null> {
|
|
||||||
const response = await this.prismaService.scheduleReplace.findUnique({
|
|
||||||
where: { etag: etag },
|
|
||||||
});
|
|
||||||
if (response == null) return null;
|
|
||||||
|
|
||||||
return plainToInstance(SetScheduleReplacerDto, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAll(): Promise<Array<SetScheduleReplacerDto>> {
|
|
||||||
const response = await this.prismaService.scheduleReplace.findMany();
|
|
||||||
|
|
||||||
return plainToInstance(SetScheduleReplacerDto, 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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,15 +2,13 @@ import { forwardRef, Module } from "@nestjs/common";
|
|||||||
import { PrismaService } from "../prisma/prisma.service";
|
import { PrismaService } from "../prisma/prisma.service";
|
||||||
import { FirebaseAdminModule } from "../firebase-admin/firebase-admin.module";
|
import { FirebaseAdminModule } from "../firebase-admin/firebase-admin.module";
|
||||||
import { UsersModule } from "src/users/users.module";
|
import { UsersModule } from "src/users/users.module";
|
||||||
import { ScheduleReplacerService } from "./schedule-replacer.service";
|
|
||||||
import { ScheduleReplacerController } from "./schedule-replacer.controller";
|
|
||||||
import { ScheduleService } from "./schedule.service";
|
import { ScheduleService } from "./schedule.service";
|
||||||
import { ScheduleController } from "./schedule.controller";
|
import { ScheduleController } from "./schedule.controller";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [forwardRef(() => UsersModule), FirebaseAdminModule],
|
imports: [forwardRef(() => UsersModule), FirebaseAdminModule],
|
||||||
providers: [PrismaService, ScheduleService, ScheduleReplacerService],
|
providers: [PrismaService, ScheduleService],
|
||||||
controllers: [ScheduleController, ScheduleReplacerController],
|
controllers: [ScheduleController],
|
||||||
exports: [ScheduleService],
|
exports: [ScheduleService],
|
||||||
})
|
})
|
||||||
export class ScheduleModule {}
|
export class ScheduleModule {}
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
import { Test, TestingModule } from "@nestjs/testing";
|
|
||||||
import { V1ScheduleService } from "./schedule.service";
|
|
||||||
import * as fs from "node:fs";
|
|
||||||
import { CacheModule } from "@nestjs/cache-manager";
|
|
||||||
import { FirebaseAdminService } from "../firebase-admin/firebase-admin.service";
|
|
||||||
import { UsersService } from "../users/users.service";
|
|
||||||
import { PrismaService } from "../prisma/prisma.service";
|
|
||||||
import { ScheduleReplacerService } from "./schedule-replacer.service";
|
|
||||||
|
|
||||||
describe("V1ScheduleService", () => {
|
|
||||||
let service: V1ScheduleService;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
return;
|
|
||||||
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [CacheModule.register()],
|
|
||||||
providers: [
|
|
||||||
V1ScheduleService,
|
|
||||||
CacheModule,
|
|
||||||
FirebaseAdminService,
|
|
||||||
UsersService,
|
|
||||||
PrismaService,
|
|
||||||
ScheduleReplacerService,
|
|
||||||
],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
service = module.get<V1ScheduleService>(V1ScheduleService);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("get group schedule", () => {
|
|
||||||
it("should return group schedule", async () => {
|
|
||||||
return;
|
|
||||||
|
|
||||||
const mainPage = fs.readFileSync("./test/mainPage").toString();
|
|
||||||
await service.updateSiteMainPage({ mainPage: mainPage });
|
|
||||||
|
|
||||||
const groupName = "ИС-214/23";
|
|
||||||
|
|
||||||
const schedule = await service.getGroup(groupName);
|
|
||||||
expect(schedule.group.name).toBe(groupName);
|
|
||||||
|
|
||||||
console.log(schedule.group.days[2].lessons[0].teacherNames);
|
|
||||||
expect(schedule.group.days[2].lessons[0].teacherNames.length).toBe(
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -2,7 +2,6 @@ import { Inject, Injectable, NotFoundException } from "@nestjs/common";
|
|||||||
import { BasicXlsDownloader } from "./internal/xls-downloader/basic-xls-downloader";
|
import { BasicXlsDownloader } from "./internal/xls-downloader/basic-xls-downloader";
|
||||||
import { Cache, CACHE_MANAGER } from "@nestjs/cache-manager";
|
import { Cache, CACHE_MANAGER } from "@nestjs/cache-manager";
|
||||||
import { plainToInstance } from "class-transformer";
|
import { plainToInstance } from "class-transformer";
|
||||||
import { ScheduleReplacerService } from "./schedule-replacer.service";
|
|
||||||
import { FirebaseAdminService } from "../firebase-admin/firebase-admin.service";
|
import { FirebaseAdminService } from "../firebase-admin/firebase-admin.service";
|
||||||
import { scheduleConstants } from "../contants";
|
import { scheduleConstants } from "../contants";
|
||||||
import {
|
import {
|
||||||
@@ -32,12 +31,10 @@ export class ScheduleService {
|
|||||||
/**
|
/**
|
||||||
* Конструктор сервиса
|
* Конструктор сервиса
|
||||||
* @param cacheManager Менеджер кэша
|
* @param cacheManager Менеджер кэша
|
||||||
* @param scheduleReplacerService Сервис замены расписания
|
|
||||||
* @param firebaseAdminService Сервис работы с Firebase
|
* @param firebaseAdminService Сервис работы с Firebase
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
|
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
|
||||||
private readonly scheduleReplacerService: ScheduleReplacerService,
|
|
||||||
private readonly firebaseAdminService: FirebaseAdminService,
|
private readonly firebaseAdminService: FirebaseAdminService,
|
||||||
) {
|
) {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@@ -57,10 +54,7 @@ export class ScheduleService {
|
|||||||
.then();
|
.then();
|
||||||
}, 60000);
|
}, 60000);
|
||||||
|
|
||||||
this.scheduleParser = new ScheduleParser(
|
this.scheduleParser = new ScheduleParser(new BasicXlsDownloader());
|
||||||
new BasicXlsDownloader(),
|
|
||||||
this.scheduleReplacerService,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,18 +86,15 @@ export class ScheduleService {
|
|||||||
|
|
||||||
if (this.cacheHash !== oldHash) {
|
if (this.cacheHash !== oldHash) {
|
||||||
if (this.scheduleUpdatedAt.valueOf() !== 0) {
|
if (this.scheduleUpdatedAt.valueOf() !== 0) {
|
||||||
const isReplaced = await this.scheduleReplacerService.hasByEtag(
|
|
||||||
schedule.etag,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.firebaseAdminService.sendByTopic("common", {
|
await this.firebaseAdminService.sendByTopic("common", {
|
||||||
data: {
|
data: {
|
||||||
type: "schedule-update",
|
type: "schedule-update",
|
||||||
replaced: isReplaced.toString(),
|
replaced: "false",
|
||||||
etag: schedule.etag,
|
etag: schedule.etag,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scheduleUpdatedAt = new Date();
|
this.scheduleUpdatedAt = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { IsArray, IsString, ValidateNested } from "class-validator";
|
import { IsArray, IsString, ValidateNested } from "class-validator";
|
||||||
|
import { plainToInstance } from "class-transformer";
|
||||||
|
import { ClassProperties } from "../../utility/class-trasformer/class-transformer-ctor";
|
||||||
|
|
||||||
// noinspection JSClassNamingConvention
|
// noinspection JSClassNamingConvention
|
||||||
export default class FCM {
|
export default class FCM {
|
||||||
@@ -17,4 +19,8 @@ export default class FCM {
|
|||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@IsString()
|
@IsString()
|
||||||
topics: Array<string>;
|
topics: Array<string>;
|
||||||
|
|
||||||
|
static fromObject(object: ClassProperties<FCM>): FCM {
|
||||||
|
return plainToInstance(FCM, object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,16 @@ import User from "./entity/user.entity";
|
|||||||
export class UsersService {
|
export class UsersService {
|
||||||
constructor(private readonly prismaService: PrismaService) {}
|
constructor(private readonly prismaService: PrismaService) {}
|
||||||
|
|
||||||
async findUnique(where: Prisma.UserWhereUniqueInput): Promise<User> {
|
async findUnique(where: Prisma.UserWhereUniqueInput): Promise<User>;
|
||||||
|
async findUnique(args: Prisma.UserFindUniqueArgs): Promise<User>;
|
||||||
|
|
||||||
|
async findUnique(
|
||||||
|
args: Prisma.UserWhereUniqueInput | Prisma.UserFindUniqueArgs,
|
||||||
|
): Promise<User> {
|
||||||
return User.fromPlain(
|
return User.fromPlain(
|
||||||
await this.prismaService.user.findUnique({ where: where }),
|
await this.prismaService.user.findUnique(
|
||||||
|
"where" in args ? args : { where: args },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,11 +26,8 @@ export class UsersService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(params: {
|
async update(args: Prisma.UserUpdateArgs): Promise<User> {
|
||||||
where: Prisma.UserWhereUniqueInput;
|
return User.fromPlain(await this.prismaService.user.update(args));
|
||||||
data: Prisma.UserUpdateInput;
|
|
||||||
}): Promise<User> {
|
|
||||||
return User.fromPlain(await this.prismaService.user.update(params));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: Prisma.UserCreateInput): Promise<User> {
|
async create(data: Prisma.UserCreateInput): Promise<User> {
|
||||||
|
|||||||
4
start.sh
4
start.sh
@@ -1,11 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DATABASE_URL="mongodb://$(cat "$MONGO_PROJECTDB_USERNAME_FILE"):$(cat "$MONGO_PROJECTDB_PASSWORD_FILE")@$MONGO_DOMAIN:$MONGO_PORT/$MONGO_INITDB_DATABASE?authMechanism=SCRAM-SHA-1"
|
DATABASE_URL="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB"
|
||||||
export DATABASE_URL
|
export DATABASE_URL
|
||||||
|
|
||||||
JWT_SECRET=$(cat "$JWT_TOKEN_FILE")
|
JWT_SECRET=$(cat "$JWT_TOKEN_FILE")
|
||||||
export JWT_SECRET
|
export JWT_SECRET
|
||||||
|
|
||||||
npx prisma db push
|
npx prisma db push --skip-generate
|
||||||
|
|
||||||
exec "$@"
|
exec "$@"
|
||||||
Reference in New Issue
Block a user