Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

알림 시스템(SSE) #56

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
33 changes: 33 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"sse": "^0.0.8",
"swagger-ui-express": "^4.6.0",
"typeorm": "^0.3.7",
"typeorm-naming-strategies": "^4.1.0",
Expand Down
3 changes: 3 additions & 0 deletions src/AppModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { LoggerMiddleware } from '@/common/middlewares';
import { CommonModule } from '@/modules/common/CommonModule';
import { SearchModule } from '@/modules/search/SearchModule';
import { WishlistModule } from '@/modules/wishlists/WishlistModule';
import { NotificationModule } from '@/modules/notification/NotificationModule';
import { EventEmitterModule } from '@nestjs/event-emitter';

@Module({
imports: [
Expand All @@ -31,6 +33,7 @@ import { WishlistModule } from '@/modules/wishlists/WishlistModule';
CommonModule,
WishlistModule,
SearchModule,
NotificationModule,
],
controllers: [],
providers: [
Expand Down
2 changes: 2 additions & 0 deletions src/config/database/DatabaseConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { User, UserStatistics, Badge, Follow } from '@/modules/users/entities';
import { Tag } from '@/modules/tags/entities/Tag';
import { WishlistItem } from '@/modules/wishlists/entities/WishlistItem';
import { WishlistFolder } from '@/modules/wishlists/entities';
import { Notify } from '@/modules/notification/entities';

@Injectable()
export class DatabaseConfigService implements TypeOrmOptionsFactory {
Expand All @@ -28,6 +29,7 @@ export class DatabaseConfigService implements TypeOrmOptionsFactory {
WishlistItem,
WishlistFolder,
Tag,
Notify,
];
}

Expand Down
20 changes: 14 additions & 6 deletions src/modules/forms/FormController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ import {
FormListResponse,
FormResponse,
} from '@/modules/forms/dtos/response';
import { NotificationService } from '@/modules/notification/NotificationService';


@UseGuards(AuthGuard('jwt'))
@Controller('forms')
export class FormController {
constructor(
private readonly formService: FormService,
private readonly formLikeService: FormLikeService,
private readonly formCommentService: FormCommentService,
private readonly notificationService: NotificationService,
) {}

@Get('/')
Expand Down Expand Up @@ -80,12 +84,12 @@ export class FormController {
async likeForm(@Req() req, @Param('id') id: number) {
const { id: userId } = req.user;

const likeResult = this.formLikeService.like(userId, id);

if (!likeResult) {
throw new BadRequestException();
const likeResult = await this.formLikeService.like(userId, id);
const form = await this.formService.findById(id);
if (likeResult) {
const type = 'LIKE';
await this.notificationService.saveNotification(type, id, form.user);
}

return { status: likeResult };
}

Expand All @@ -109,6 +113,8 @@ export class FormController {
if (!result) {
throw new BadRequestException();
}
const type = 'COMMENT';
await this.notificationService.saveNotification(type, id, result.form.user);

return new FormCommentResponse(result);
}
Expand All @@ -122,11 +128,13 @@ export class FormController {
const { id } = req.user;

const result = await this.formCommentService.updateComment(commentId, id, request);

if (!result) {
throw new BadRequestException();
}

const type = 'COMMENT';
await this.notificationService.saveNotification(type, id, result.form.user);

return new FormCommentResponse(result);
}

Expand Down
2 changes: 2 additions & 0 deletions src/modules/forms/FormModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { FormService, FormCommentService, FormLikeService } from '@/modules/form
import { TagModule } from '@/modules/tags/TagModule';
import { CommonModule } from '@/modules/common/CommonModule';
import { TourInfoModule } from '@/modules/tourInfos/TourInfoModule';
import { NotificationModule } from '@/modules/notification/NotificationModule';

@Module({
imports: [
TypeOrmModule.forFeature([Form, FormLike, FormComment]),
TagModule,
CommonModule,
TourInfoModule,
NotificationModule,
],
controllers: [FormController],
providers: [FormService, FormLikeService, FormCommentService],
Expand Down
1 change: 1 addition & 0 deletions src/modules/forms/services/FormService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class FormService {
return this.formRepository.find({ relations: ['tags', 'user'] });
}


public async sortByViews(): Promise<Form[]> {
const [list, count] = await this.formRepository.findAndCount({
order: { viewCount: 'DESC' },
Expand Down
26 changes: 26 additions & 0 deletions src/modules/notification/NotificationController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Controller, Sse, UseGuards, Param, Req } from '@nestjs/common';
import { NotificationService } from '@/modules/notification/NotificationService';
import { AuthGuard } from '@nestjs/passport';
import { interval, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface MessageEvent {
data: string | object;
userId: number;
type?: string;
retry?: number;
}

@UseGuards(AuthGuard('jwt'))
@Controller('/notify')
export class NotificationController {
constructor(private readonly notificationService: NotificationService) {}

@Sse('')
async sse(@Req() req): Promise<Observable<MessageEvent>> {
const { id } = req.user;
const notifies = await this.notificationService.getNotification(id);
return interval(1000).pipe(map(() => ({ data: { notifies } } as MessageEvent)));
}
//
}
14 changes: 14 additions & 0 deletions src/modules/notification/NotificationModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { forwardRef, Module } from '@nestjs/common';
import { NotificationService } from '@/modules/notification/NotificationService';
import { NotificationController } from '@/modules/notification/NotificationController';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Notify } from '@/modules/notification/entities';
import { UserModule } from '@/modules/users/UserModule';

@Module({
imports: [TypeOrmModule.forFeature([Notify])],
controllers: [NotificationController],
providers: [NotificationService],
exports: [NotificationService, TypeOrmModule],
})
export class NotificationModule {}
29 changes: 29 additions & 0 deletions src/modules/notification/NotificationService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Notify } from '@/modules/notification/entities';
import { UserService } from '@/modules/users/services';
import { User } from '@/modules/users/entities';

@Injectable()
export class NotificationService {
constructor(
@InjectRepository(Notify)
private readonly notifyRepository: Repository<Notify>,
) {}

public async saveNotification(type: string, data: number, target: User): Promise<Notify> {
const notify = new Notify();
notify.type = type;
notify.data = data;
notify.user = target;
console.log(notify);
return this.notifyRepository.save(notify);
}

async getNotification(userId: number): Promise<Notify[]> {
return await this.notifyRepository.find({
where: { user: { id: userId } },
});
}
}
18 changes: 18 additions & 0 deletions src/modules/notification/entities/Notify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from '@/modules/users/entities';

@Entity({ name: 'notify' })
export class Notify {
@PrimaryGeneratedColumn({ unsigned: true })
id!: number;

@ManyToOne(() => User)
@JoinColumn()
user!: User;

@Column({ nullable: false })
type!: string;

@Column({ unsigned: true })
data!: number;
}
1 change: 1 addition & 0 deletions src/modules/notification/entities/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Notify';
3 changes: 2 additions & 1 deletion src/modules/search/SearchModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { SearchService } from '@/modules/search/SearchService';
import { FormModule } from '@/modules/forms/FormModule';
import { TourInfoModule } from '@/modules/tourInfos/TourInfoModule';
import { UserModule } from '@/modules/users/UserModule';
import { TagModule } from '@/modules/tags/TagModule';

@Module({
imports: [FormModule, TourInfoModule, UserModule],
imports: [FormModule, TourInfoModule, UserModule, TagModule],
controllers: [SearchController],
providers: [SearchService],
})
Expand Down
3 changes: 3 additions & 0 deletions src/modules/search/SearchService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { SearchQuery } from '@/modules/search/dto';
import { TourInfo } from '@/modules/tourInfos/entities';
import { User } from '../users/entities';
import { Form } from '@/modules/forms/entities';
import { TagService } from '@/modules/tags/TagService';

@Injectable()
export class SearchService {
constructor(
private readonly formService: FormService,
private readonly tagService: TagService,
private readonly tourInfoService: TourInfoService,
private readonly userService: UserService,
) {}
Expand All @@ -23,6 +25,7 @@ export class SearchService {
let tag: any[];
if (query.tag) {
tag = query.tag.split(',');
tag.forEach((tag) => this.tagService.findByName(tag));
}
return await this.formService.getAll({ tag });
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/tags/TagModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { Tag } from '@/modules/tags/entities/Tag';

@Module({
exports: [TagService],
exports: [TagService, TypeOrmModule],
imports: [TypeOrmModule.forFeature([Tag])],
providers: [TagService],
})
Expand Down
15 changes: 13 additions & 2 deletions src/modules/tags/TagService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ export class TagService {
private readonly tagRepository: Repository<Tag>,
) {}

private async findByName(name: string): Promise<Tag | null> {
return this.tagRepository.findOne({ where: { name } });
public async findByName(name: string): Promise<Tag | null> {
const tag = await this.tagRepository.findOne({ where: { name } });
if (tag) {
const viewsIncreased = await this.tagRepository.increment({ name }, 'viewCount', 1);
}
return tag;
}

public async saveTag(name: string): Promise<Tag> {
Expand All @@ -26,4 +30,11 @@ export class TagService {

return isExist;
}

public async sortByViews(): Promise<Tag[]> {
const [list, count] = await this.tagRepository.findAndCount({
order: { viewCount: 'DESC' },
});
return list;
}
}
10 changes: 10 additions & 0 deletions src/modules/tags/dto/TagListResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Tag } from '@/modules/tags/entities/Tag';
import { TagResponse } from '@/modules/tags/dto/TagResponse';

export class TagListResponse {
list: TagResponse[];

constructor(record: Tag[]) {
this.list = record.map((tag) => new TagResponse(tag));
}
}
15 changes: 15 additions & 0 deletions src/modules/tags/dto/TagResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Tag } from '@/modules/tags/entities/Tag';

export class TagResponse {
public id: number;

public name: string;

public viewCount: number;

constructor(record: Tag) {
this.id = record.id;
this.name = record.name;
this.viewCount = record.viewCount;
}
}
1 change: 1 addition & 0 deletions src/modules/tags/dto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './TagListResponse';
Loading